Direct-BT v3.3.0-1-gc2d430c
Direct-BT - Direct Bluetooth Programming.
dbt_repeater00.cpp
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#include <cstring>
26#include <string>
27#include <memory>
28#include <cstdint>
29#include <fstream>
30#include <iostream>
31
32#include <cinttypes>
33
34#include <pthread.h>
35#include <csignal>
36
37#include <jau/cpp_lang_util.hpp>
39#include <jau/basic_algos.hpp>
40#include <jau/darray.hpp>
41
43
44extern "C" {
45 #include <unistd.h>
46}
47
48#include "dbt_constants.hpp"
49
50using namespace direct_bt;
51using namespace jau;
52
53/** \file
54 * This _dbt_repeater00_ C++ repeater example implementing a GATT repeater,
55 * i.e. forwarding client requests to a GATT server and passing the results back.
56 *
57 * The repeater can be used in between an existing Bluetooth LE client and server,
58 * acting as a forwarder and to analyze the GATT client/server protocol.
59 *
60 * ### dbt_repeater00 Invocation Examples:
61 * Using `scripts/run-dbt_repeater00.sh` from `dist` directory:
62 *
63 * * Connection to server `TAIDOC TD1107` using adapter `DC:FB:48:00:90:19`; Serving client as `TAIDOC TD1108` using adapter `00:1A:7D:DA:71:03`; Using ENC_ONLY (JUST_WORKS) encryption.
64 * ~~~
65 * ../scripts/run-dbt_repeater00.sh -adapterToServer DC:FB:48:00:90:19 -adapterToClient 00:1A:7D:DA:71:03 -server 'TAIDOC TD1107' -nameToClient 'TAIDOC TD1108' -seclevelToServer 'TAIDOC TD1107' 2 -seclevelToClient 2 -quiet
66 * ~~~
67 */
68
69static uint64_t timestamp_t0;
70
72static BTMode btMode = BTMode::DUAL;
73
74//
75// To Server Settings (acting as client)
76//
77static EUI48 adapterToServerAddr = EUI48::ALL_DEVICE;
80static std::atomic<int> serverDeviceReadyCount = 0;
81
82static void connectToDiscoveredServer(BTDeviceRef device); // NOLINT(performance-unnecessary-value-param): Pass-by-value out-of-thread
83static void processReadyToServer(const BTDeviceRef& device);
84static void removeDeviceToServer(BTDeviceRef device); // NOLINT(performance-unnecessary-value-param): Pass-by-value out-of-thread
85static bool startDiscoveryToServer(BTAdapter *a, std::string msg); // NOLINT(performance-unnecessary-value-param): Pass-by-value out-of-thread
86
87static DiscoveryPolicy discoveryPolicy = DiscoveryPolicy::PAUSE_CONNECTED_UNTIL_DISCONNECTED;
88static const bool le_scan_active = true; // default value
89static const uint16_t le_scan_interval = 24; // default value
90static const uint16_t le_scan_window = 24; // default value
91static const uint8_t filter_policy = 0; // default value
92static const uint16_t adv_interval_min=640;
93static const uint16_t adv_interval_max=640;
94static const AD_PDU_Type adv_type=AD_PDU_Type::ADV_IND;
95static const uint8_t adv_chan_map=0x07;
96
97//
98// To Client Settings (acting as server)
99//
100static EUI48 adapterToClientAddr = EUI48::ALL_DEVICE;
101static bool adapterToClientUseSC = true;
102static std::string adapterToClientName = "repeater0";
103static std::string adapterToClientShortName = "repeater0";
104static uint16_t max_att_mtu_to_client = 512+1;
105static BTSecurityLevel adapterToClientSecLevel = BTSecurityLevel::UNSET;
108static jau::nsize_t MAX_SERVED_CONNECTIONS = 0; // unlimited
110
111static bool startAdvertisingToClient(const BTAdapterRef& a, const std::string& msg);
112static bool stopAdvertisingToClient(const BTAdapterRef& a, const std::string& msg);
113static void processDisconnectedDeviceToClient(BTDeviceRef device); // NOLINT(performance-unnecessary-value-param): Pass-by-value out-of-thread
114
115static bool QUIET = false;
116
117//
118// To Server Settings (acting as client)
119//
120
122
123 void adapterSettingsChanged(BTAdapter &a, const AdapterSetting oldmask, const AdapterSetting newmask,
124 const AdapterSetting changedmask, const uint64_t timestamp) override {
125 const bool initialSetting = AdapterSetting::NONE == oldmask;
126 if( initialSetting ) {
127 fprintf_td(stderr, "****** To Server: SETTINGS_INITIAL: %s -> %s, changed %s\n", to_string(oldmask).c_str(),
128 to_string(newmask).c_str(), to_string(changedmask).c_str());
129 } else {
130 fprintf_td(stderr, "****** To Server: SETTINGS_CHANGED: %s -> %s, changed %s\n", to_string(oldmask).c_str(),
131 to_string(newmask).c_str(), to_string(changedmask).c_str());
132 }
133 fprintf_td(stderr, "To Server: Status BTAdapter:\n");
134 fprintf_td(stderr, "%s\n", a.toString().c_str());
135 (void)timestamp;
136
137 if( !initialSetting &&
138 isAdapterSettingBitSet(changedmask, AdapterSetting::POWERED) &&
139 isAdapterSettingBitSet(newmask, AdapterSetting::POWERED) )
140 {
141 std::thread sd(::startDiscoveryToServer, &a, "powered-on"); // @suppress("Invalid arguments")
142 sd.detach();
143 }
144 }
145
146 void discoveringChanged(BTAdapter &a, const ScanType currentMeta, const ScanType changedType, const bool changedEnabled, const DiscoveryPolicy policy, const uint64_t timestamp) override {
147 fprintf_td(stderr, "****** To Server: DISCOVERING: meta %s, changed[%s, enabled %d, policy %s]: %s\n",
148 to_string(currentMeta).c_str(), to_string(changedType).c_str(), changedEnabled, to_string(policy).c_str(), a.toString().c_str());
149 (void)timestamp;
150 }
151
152 bool deviceFound(const BTDeviceRef& device, const uint64_t timestamp) override {
153 (void)timestamp;
154
156 BTDeviceRegistry::isWaitingForDevice(device->getAddressAndType().address, device->getName())
157 )
158 {
159 fprintf_td(stderr, "****** To Server: FOUND__-0: Connecting %s\n", device->toString(true).c_str());
160 {
161 const uint64_t td = getCurrentMilliseconds() - timestamp_t0; // adapter-init -> now
162 fprintf_td(stderr, "PERF: adapter-init -> FOUND__-0 %" PRIu64 " ms\n", td);
163 }
164 std::thread dc(::connectToDiscoveredServer, device); // @suppress("Invalid arguments")
165 dc.detach();
166 return true;
167 } else {
168 if( !QUIET ) {
169 fprintf_td(stderr, "****** To Server: FOUND__-1: NOP %s\n", device->toString(true).c_str());
170 }
171 return false;
172 }
173 }
174
175 void deviceConnected(const BTDeviceRef& device, const bool discovered, const uint64_t timestamp) override {
176 fprintf_td(stderr, "****** To Server: CONNECTED (discovered %d): %s\n", discovered, device->toString(true).c_str());
177 (void)discovered;
178 (void)timestamp;
179 }
180
181 void devicePairingState(const BTDeviceRef& device, const SMPPairingState state, const PairingMode mode, const uint64_t timestamp) override {
182 fprintf_td(stderr, "****** To Server: PAIRING STATE: state %s, mode %s, %s\n",
183 to_string(state).c_str(), to_string(mode).c_str(), device->toString().c_str());
184 (void)timestamp;
185 switch( state ) {
186 case SMPPairingState::NONE:
187 // next: deviceReady(..)
188 break;
189 case SMPPairingState::FAILED: {
190 const bool res = SMPKeyBin::remove(CLIENT_KEY_PATH, *device);
191 fprintf_td(stderr, "****** To Server: PAIRING_STATE: state %s; Remove key file %s, res %d\n",
192 to_string(state).c_str(), SMPKeyBin::getFilename(CLIENT_KEY_PATH, *device).c_str(), res);
193 // next: deviceReady() or deviceDisconnected(..)
194 } break;
195 case SMPPairingState::REQUESTED_BY_RESPONDER:
196 // next: FEATURE_EXCHANGE_STARTED
197 break;
198 case SMPPairingState::FEATURE_EXCHANGE_STARTED:
199 // next: FEATURE_EXCHANGE_COMPLETED
200 break;
201 case SMPPairingState::FEATURE_EXCHANGE_COMPLETED:
202 // next: PASSKEY_EXPECTED... or KEY_DISTRIBUTION
203 break;
204 case SMPPairingState::PASSKEY_EXPECTED: {
205 const BTSecurityRegistry::Entry* sec = BTSecurityRegistry::getStartOf(device->getAddressAndType().address, device->getName());
206 if( nullptr != sec && sec->getPairingPasskey() != BTSecurityRegistry::Entry::NO_PASSKEY ) {
207 std::thread dc(&BTDevice::setPairingPasskey, device, static_cast<uint32_t>( sec->getPairingPasskey() ));
208 dc.detach();
209 } else {
210 std::thread dc(&BTDevice::setPairingPasskey, device, 0);
211 // 3s disconnect: std::thread dc(&BTDevice::setPairingPasskeyNegative, device);
212 dc.detach();
213 }
214 // next: KEY_DISTRIBUTION or FAILED
215 } break;
216 case SMPPairingState::NUMERIC_COMPARE_EXPECTED: {
217 const BTSecurityRegistry::Entry* sec = BTSecurityRegistry::getStartOf(device->getAddressAndType().address, device->getName());
218 if( nullptr != sec ) {
219 std::thread dc(&BTDevice::setPairingNumericComparison, device, sec->getPairingNumericComparison());
220 dc.detach();
221 } else {
222 std::thread dc(&BTDevice::setPairingNumericComparison, device, false);
223 dc.detach();
224 }
225 // next: KEY_DISTRIBUTION or FAILED
226 } break;
227 case SMPPairingState::OOB_EXPECTED:
228 // FIXME: ABORT
229 break;
230 case SMPPairingState::KEY_DISTRIBUTION:
231 // next: COMPLETED or FAILED
232 break;
233 case SMPPairingState::COMPLETED:
234 // next: deviceReady(..)
235 break;
236 default: // nop
237 break;
238 }
239 }
240
241 void deviceReady(const BTDeviceRef& device, const uint64_t timestamp) override {
242 (void)timestamp;
244 BTDeviceRegistry::isWaitingForDevice(device->getAddressAndType().address, device->getName())
245 )
246 {
248 fprintf_td(stderr, "****** To Server: READY-0: Processing[%d] %s\n", serverDeviceReadyCount.load(), device->toString(true).c_str());
249 processReadyToServer(device); // AdapterStatusListener::deviceReady() explicitly allows prolonged and complex code execution!
250 } else {
251 fprintf_td(stderr, "****** To Server: READY-1: NOP %s\n", device->toString(true).c_str());
252 }
253 }
254
255 void deviceDisconnected(const BTDeviceRef& device, const HCIStatusCode reason, const uint16_t handle, const uint64_t timestamp) override {
256 fprintf_td(stderr, "****** To Server: DISCONNECTED: Reason 0x%X (%s), old handle %s: %s\n",
257 static_cast<uint8_t>(reason), to_string(reason).c_str(),
258 to_hexstring(handle).c_str(), device->toString(true).c_str());
259 (void)timestamp;
260 {
262 connectedDeviceToServer = nullptr;
263 }
264 std::thread dc(::removeDeviceToServer, device); // @suppress("Invalid arguments")
265 dc.detach();
266 }
267
268 std::string toString() const noexcept override {
269 return "MyAdapterClientStatusListener[this "+to_hexstring(this)+"]";
270 }
271
272};
273
275 public:
276
278
282 }
283
284 void notificationReceived(const BTDeviceRef& source, const uint16_t char_handle,
285 const TROOctets& char_value, const uint64_t timestamp) override
286 {
287 (void)timestamp;
288 BTDeviceRef devToClient = getToClient();
289 std::string devToClientS = nullptr != devToClient ? devToClient->getAddressAndType().address.toString() : "nil";
290 std::string devFromServerS = source->getAddressAndType().address.toString();
291
292 fprintf_td(stderr, "%s* -> %s : Notify: handle %s\n",
293 devFromServerS.c_str(), devToClientS.c_str(), jau::to_hexstring(char_handle).c_str());
294 fprintf_td(stderr, " raw : %s\n", char_value.toString().c_str());
295 fprintf_td(stderr, " utf8: %s\n", jau::dfa_utf8_decode(char_value.get_ptr(), char_value.size()).c_str());
296 fprintf_td(stderr, "\n");
297 std::shared_ptr<BTGattHandler> gh = nullptr != devToClient ? devToClient->getGattHandler() : nullptr;
298 if( nullptr != gh ) {
299 gh->sendNotification(char_handle, char_value);
300 }
301 }
302
303 void indicationReceived(const BTDeviceRef& source, const uint16_t char_handle,
304 const TROOctets& char_value, const uint64_t timestamp,
305 const bool confirmationSent) override
306 {
307 (void)timestamp;
308 BTDeviceRef devToClient = getToClient();
309 std::string devToClientS = nullptr != devToClient ? devToClient->getAddressAndType().address.toString() : "nil";
310 std::string devFromServerS = source->getAddressAndType().address.toString();
311
312 fprintf_td(stderr, "%s* -> %s : Indication: handle %s, confirmed %d\n",
313 devFromServerS.c_str(), devToClientS.c_str(), jau::to_hexstring(char_handle).c_str(), confirmationSent);
314 fprintf_td(stderr, " raw : %s\n", char_value.toString().c_str());
315 fprintf_td(stderr, " utf8: %s\n", jau::dfa_utf8_decode(char_value.get_ptr(), char_value.size()).c_str());
316 fprintf_td(stderr, "\n");
317 std::shared_ptr<BTGattHandler> gh = nullptr != devToClient ? devToClient->getGattHandler() : nullptr;
318 if( nullptr != gh ) {
319 gh->sendIndication(char_handle, char_value);
320 }
321 }
322
323 void mtuResponse(const uint16_t clientMTU,
324 const AttPDUMsg& pduReply,
325 const AttErrorRsp::ErrorCode error_reply,
326 const uint16_t serverMTU,
327 const uint16_t usedMTU,
328 const BTDeviceRef& serverReplier,
329 const BTDeviceRef& clientRequester) override {
330 std::string serverReplierS = serverReplier->getAddressAndType().address.toString();
331 std::string clientRequesterS = nullptr != clientRequester ? clientRequester->getAddressAndType().address.toString() : "nil";
332
333 fprintf_td(stderr, "%s <-> %s*: MTU: client %u -> %s, server %u -> used %u\n",
334 clientRequesterS.c_str(), serverReplierS.c_str(),
335 clientMTU, AttErrorRsp::getErrorCodeString(error_reply).c_str(), serverMTU, usedMTU);
336 if( AttErrorRsp::ErrorCode::NO_ERROR != error_reply ) {
337 fprintf_td(stderr, " pdu : %s\n", pduReply.toString().c_str());
338 }
339 fprintf_td(stderr, "\n");
340 }
341
342 void writeRequest(const uint16_t handle,
343 const jau::TROOctets& data,
344 const jau::darray<Section>& sections,
345 const bool with_response,
346 const BTDeviceRef& serverDest,
347 const BTDeviceRef& clientSource) override {
348 std::string serverDestS = serverDest->getAddressAndType().address.toString();
349 std::string clientSourceS = nullptr != clientSource ? clientSource->getAddressAndType().address.toString() : "nil";
350
351 fprintf_td(stderr, "%s -> %s*: Write-Req: handle %s, with_response %d\n",
352 clientSourceS.c_str(), serverDestS.c_str(), jau::to_hexstring(handle).c_str(), with_response);
353 fprintf_td(stderr, " raw : %s\n", data.toString().c_str());
354 fprintf_td(stderr, " utf8: %s\n", jau::dfa_utf8_decode(data.get_ptr(), data.size()).c_str());
355 fprintf_td(stderr, " sections: ");
356 for(Section s : sections) {
357 fprintf(stderr, "%s, ", s.toString().c_str());
358 }
359 fprintf(stderr, "\n");
360 fprintf_td(stderr, "\n");
361 }
362
363 void writeResponse(const AttPDUMsg& pduReply,
364 const AttErrorRsp::ErrorCode error_code,
365 const BTDeviceRef& serverSource,
366 const BTDeviceRef& clientDest) override {
367 std::string serverSourceS = serverSource->getAddressAndType().address.toString();
368 std::string clientDestS = nullptr != clientDest ? clientDest->getAddressAndType().address.toString() : "nil";
369
370 fprintf_td(stderr, "%s* -> %s : Write-Rsp: %s\n",
371 serverSourceS.c_str(), clientDestS.c_str(), AttErrorRsp::getErrorCodeString(error_code).c_str());
372 fprintf_td(stderr, " pdu : %s\n", pduReply.toString().c_str());
373 fprintf_td(stderr, "\n");
374 }
375
376
377 void readResponse(const uint16_t handle,
378 const uint16_t value_offset,
379 const AttPDUMsg& pduReply,
380 const AttErrorRsp::ErrorCode error_reply,
381 const jau::TROOctets& data_reply,
382 const BTDeviceRef& serverReplier,
383 const BTDeviceRef& clientRequester) override {
384 std::string serverReplierS = serverReplier->getAddressAndType().address.toString();
385 std::string clientRequesterS = nullptr != clientRequester ? clientRequester->getAddressAndType().address.toString() : "nil";
386
387 fprintf_td(stderr, "%s <-> %s*: Read: handle %s, value_offset %d -> %s\n",
388 clientRequesterS.c_str(), serverReplierS.c_str(),
389 jau::to_hexstring(handle).c_str(), value_offset, AttErrorRsp::getErrorCodeString(error_reply).c_str());
390 if( 0 < data_reply.size() ) {
391 fprintf_td(stderr, " raw : %s\n", data_reply.toString().c_str());
392 fprintf_td(stderr, " utf8: %s\n", jau::dfa_utf8_decode(data_reply.get_ptr(), data_reply.size()).c_str());
393 } else {
394 fprintf_td(stderr, " pdu : %s\n", pduReply.toString().c_str());
395 }
396 fprintf_td(stderr, "\n");
397 }
398
399};
400
401static void connectToDiscoveredServer(BTDeviceRef device) { // NOLINT(performance-unnecessary-value-param): Pass-by-value out-of-thread
402 fprintf_td(stderr, "****** To Server: Connecting Device: Start %s\n", device->toString().c_str());
403
404 const BTSecurityRegistry::Entry* sec = BTSecurityRegistry::getStartOf(device->getAddressAndType().address, device->getName());
405 if( nullptr != sec ) {
406 fprintf_td(stderr, "****** To Server: Connecting Device: Found SecurityDetail %s for %s\n", sec->toString().c_str(), device->toString().c_str());
407 } else {
408 fprintf_td(stderr, "****** To Server: Connecting Device: No SecurityDetail for %s\n", device->toString().c_str());
409 }
410 const BTSecurityLevel req_sec_level = nullptr != sec ? sec->getSecLevel() : BTSecurityLevel::UNSET;
411 HCIStatusCode res = device->uploadKeys(CLIENT_KEY_PATH, req_sec_level, true /* verbose_ */);
412 fprintf_td(stderr, "****** Connecting Device: BTDevice::uploadKeys(...) result %s\n", to_string(res).c_str());
413 if( HCIStatusCode::SUCCESS != res ) {
414 if( nullptr != sec ) {
415 if( sec->isSecurityAutoEnabled() ) {
416 bool r = device->setConnSecurityAuto( sec->getSecurityAutoIOCap() );
417 fprintf_td(stderr, "****** To Server: Connecting Device: Using SecurityDetail.SEC AUTO %s, set OK %d\n", sec->toString().c_str(), r);
418 } else if( sec->isSecLevelOrIOCapSet() ) {
419 bool r = device->setConnSecurity( sec->getSecLevel(), sec->getIOCap() );
420 fprintf_td(stderr, "****** To Server: Connecting Device: Using SecurityDetail.Level+IOCap %s, set OK %d\n", sec->toString().c_str(), r);
421 } else {
422 bool r = device->setConnSecurityAuto( SMPIOCapability::KEYBOARD_ONLY );
423 fprintf_td(stderr, "****** To Server: Connecting Device: Setting SEC AUTO security detail w/ KEYBOARD_ONLY (%s) -> set OK %d\n", sec->toString().c_str(), r);
424 }
425 } else {
426 bool r = device->setConnSecurityAuto( SMPIOCapability::KEYBOARD_ONLY );
427 fprintf_td(stderr, "****** To Server: Connecting Device: Setting SEC AUTO security detail w/ KEYBOARD_ONLY -> set OK %d\n", r);
428 }
429 }
430 std::shared_ptr<const EInfoReport> eir = device->getEIR();
431 fprintf_td(stderr, "To Server: EIR-1 %s\n", device->getEIRInd()->toString().c_str());
432 fprintf_td(stderr, "To Server: EIR-2 %s\n", device->getEIRScanRsp()->toString().c_str());
433 fprintf_td(stderr, "To Server: EIR-+ %s\n", eir->toString().c_str());
434
435 uint16_t conn_interval_min = (uint16_t)12;
436 uint16_t conn_interval_max = (uint16_t)12;
437 const uint16_t conn_latency = (uint16_t)0;
438 if( eir->isSet(EIRDataType::CONN_IVAL) ) {
439 eir->getConnInterval(conn_interval_min, conn_interval_max);
440 }
441 const uint16_t supervision_timeout = (uint16_t) getHCIConnSupervisorTimeout(conn_latency, (int) ( conn_interval_max * 1.25 ) /* ms */);
442 res = device->connectLE(le_scan_interval, le_scan_window, conn_interval_min, conn_interval_max, conn_latency, supervision_timeout);
443 fprintf_td(stderr, "****** To Server: Connecting Device: End result %s of %s\n", to_string(res).c_str(), device->toString().c_str());
444}
445
446static void processReadyToServer(const BTDeviceRef& device) {
447 fprintf_td(stderr, "****** To Server: Processing Ready Device: Start %s\n", device->toString().c_str());
448
449 SMPKeyBin::createAndWrite(*device, CLIENT_KEY_PATH, true /* verbose */);
450
451 bool success = false;
452
453 if( device->getAdapter().getBTMajorVersion() > 4 ) {
454 LE_PHYs Tx { LE_PHYs::LE_2M }, Rx { LE_PHYs::LE_2M };
455 HCIStatusCode res = device->setConnectedLE_PHY(Tx, Rx);
456 fprintf_td(stderr, "****** To Server: Set Connected LE PHY: status %s: Tx %s, Rx %s\n",
457 to_string(res).c_str(), to_string(Tx).c_str(), to_string(Rx).c_str());
458 }
459 {
460 LE_PHYs resTx, resRx;
461 HCIStatusCode res = device->getConnectedLE_PHY(resTx, resRx);
462 fprintf_td(stderr, "****** To Server: Got Connected LE PHY: status %s: Tx %s, Rx %s\n",
463 to_string(res).c_str(), to_string(resTx).c_str(), to_string(resRx).c_str());
464 }
465
466 //
467 // GATT Service Processing
468 //
469 fprintf_td(stderr, "****** To Server: Processing Ready Device: GATT start: %s\n", device->getAddressAndType().toString().c_str());
470 try {
471 std::shared_ptr<BTGattHandler> gh = device->getGattHandler();
472 gh->addCharListener( std::make_shared<NativeGattToServerCharListener>() );
473
474 if( nullptr != adapterToClient ) {
477 if( !startAdvertisingToClient(adapterToClient, "processReadyToServer") ) {
478 device->disconnect(HCIStatusCode::REMOTE_USER_TERMINATED_CONNECTION);
479 } else {
480 success = true;
481 }
482 }
483 } catch ( std::exception & e ) {
484 fprintf_td(stderr, "****** To Server: Processing Ready Device: Exception caught for %s: %s\n", device->toString().c_str(), e.what());
485 }
486
487 fprintf_td(stderr, "****** To Server: Processing Ready Device: End-1: Success %d on %s\n", success, device->toString().c_str());
488
489 if( success ) {
490 BTDeviceRegistry::addToProcessedDevices(device->getAddressAndType(), device->getName());
491 }
492
493}
494
495static void removeDeviceToServer(BTDeviceRef device) { // NOLINT(performance-unnecessary-value-param): Pass-by-value out-of-thread
496 fprintf_td(stderr, "****** To Server: Remove Device: %s\n", device->getAddressAndType().toString().c_str());
497
498 {
499 stopAdvertisingToClient(adapterToClient, "removeDeviceToServer");
501 if( nullptr != connectedDeviceToClient ) {
502 connectedDeviceToClient->disconnect(HCIStatusCode::CONNECTION_TERMINATED_BY_LOCAL_HOST);
503 }
504 }
505 device->remove();
506}
507
508static void resetConnectionToServer(BTDeviceRef device) { // NOLINT(performance-unnecessary-value-param): Pass-by-value out-of-thread
509 fprintf_td(stderr, "****** To Server: Disconnected: %s\n", device->toString().c_str());
510 device->disconnect(HCIStatusCode::DISCONNECTED);
511
512 BTAdapter& a = device->getAdapter();
513 fprintf_td(stderr, "****** To Server: Power off: %s\n", a.toString().c_str());
514 if( a.setPowered(false) ) {
515 fprintf_td(stderr, "****** To Server: Power on: %s\n", a.toString().c_str());
516 if( a.setPowered(true) ) {
517 startDiscoveryToServer(&a, "resetConnectionToServer");
518 }
519 }
520}
521
522static bool startDiscoveryToServer(BTAdapter *a, std::string msg) { // NOLINT(performance-unnecessary-value-param): Pass-by-value out-of-thread
523 if( adapterToServerAddr != EUI48::ALL_DEVICE && adapterToServerAddr != a->getAddressAndType().address ) {
524 fprintf_td(stderr, "****** To Server: Start discovery (%s): Adapter not selected: %s\n", msg.c_str(), a->toString().c_str());
525 return false;
526 }
528 fprintf_td(stderr, "****** To Server: Start discovery (%s) result: %s: %s\n", msg.c_str(), to_string(status).c_str(), a->toString().c_str());
529 return HCIStatusCode::SUCCESS == status;
530}
531
532static bool initAdapterToServer(std::shared_ptr<BTAdapter>& adapter) {
533 if( adapterToServerAddr != EUI48::ALL_DEVICE && adapterToServerAddr != adapter->getAddressAndType().address ) {
534 fprintf_td(stderr, "initAdapterToServer: Adapter not selected: %s\n", adapter->toString().c_str());
535 return false;
536 }
537 // Initialize with defaults and power-on
538 if( !adapter->isInitialized() ) {
539 HCIStatusCode status = adapter->initialize( btMode, true );
540 if( HCIStatusCode::SUCCESS != status ) {
541 fprintf_td(stderr, "initAdapterToServer: Adapter initialization failed: %s: %s\n",
542 to_string(status).c_str(), adapter->toString().c_str());
543 return false;
544 }
545 } else if( !adapter->setPowered( true ) ) {
546 fprintf_td(stderr, "initAdapterToServer: Already initialized adapter power-on failed:: %s\n", adapter->toString().c_str());
547 return false;
548 }
549 // adapter is powered-on
550 fprintf_td(stderr, "initAdapterToServer: %s\n", adapter->toString().c_str());
551 {
552 const LE_Features le_feats = adapter->getLEFeatures();
553 fprintf_td(stderr, "initAdapterToServer: LE_Features %s\n", to_string(le_feats).c_str());
554 }
555 if( adapter->getBTMajorVersion() > 4 ) {
556 LE_PHYs Tx { LE_PHYs::LE_2M }, Rx { LE_PHYs::LE_2M };
557 HCIStatusCode res = adapter->setDefaultLE_PHY(Tx, Rx);
558 fprintf_td(stderr, "initAdapterToServer: Set Default LE PHY: status %s: Tx %s, Rx %s\n",
559 to_string(res).c_str(), to_string(Tx).c_str(), to_string(Rx).c_str());
560 }
561 std::shared_ptr<AdapterStatusListener> asl(new AdapterToServerStatusListener());
562 adapter->addStatusListener( asl );
563
564 if( !startDiscoveryToServer(adapter.get(), "initAdapterToServer") ) {
565 adapter->removeStatusListener( asl );
566 return false;
567 }
568 return true;
569}
570
571//
572// To Client Settings (acting as server)
573//
574
576
577 void adapterSettingsChanged(BTAdapter &a, const AdapterSetting oldmask, const AdapterSetting newmask,
578 const AdapterSetting changedmask, const uint64_t timestamp) override {
579 const bool initialSetting = AdapterSetting::NONE == oldmask;
580 if( initialSetting ) {
581 fprintf_td(stderr, "****** To Client: SETTINGS_INITIAL: %s -> %s, changed %s\n", to_string(oldmask).c_str(),
582 to_string(newmask).c_str(), to_string(changedmask).c_str());
583 } else {
584 fprintf_td(stderr, "****** To Client: SETTINGS_CHANGED: %s -> %s, changed %s\n", to_string(oldmask).c_str(),
585 to_string(newmask).c_str(), to_string(changedmask).c_str());
586 }
587 fprintf_td(stderr, "To Client: Status BTAdapter:\n");
588 fprintf_td(stderr, "%s\n", a.toString().c_str());
589 (void)timestamp;
590 }
591
592 void discoveringChanged(BTAdapter &a, const ScanType currentMeta, const ScanType changedType, const bool changedEnabled, const DiscoveryPolicy policy, const uint64_t timestamp) override {
593 fprintf_td(stderr, "****** To Client: DISCOVERING: meta %s, changed[%s, enabled %d, policy %s]: %s\n",
594 to_string(currentMeta).c_str(), to_string(changedType).c_str(), changedEnabled, to_string(policy).c_str(), a.toString().c_str());
595 (void)timestamp;
596 }
597
598 bool deviceFound(const BTDeviceRef& device, const uint64_t timestamp) override {
599 (void)timestamp;
600
601 fprintf_td(stderr, "****** To Client: FOUND__-1: NOP %s\n", device->toString(true).c_str());
602 return false;
603 }
604
605 void deviceConnected(const BTDeviceRef& device, const bool discovered, const uint64_t timestamp) override {
606 fprintf_td(stderr, "****** To Client: CONNECTED (discovered %d): %s\n", discovered, device->toString(true).c_str());
607 (void)discovered;
608 (void)timestamp;
609 }
610
611 void devicePairingState(const BTDeviceRef& device, const SMPPairingState state, const PairingMode mode, const uint64_t timestamp) override {
612 fprintf_td(stderr, "****** To Client: PAIRING STATE: state %s, mode %s, %s\n",
613 to_string(state).c_str(), to_string(mode).c_str(), device->toString().c_str());
614 (void)timestamp;
615 switch( state ) {
616 case SMPPairingState::NONE:
617 // next: deviceReady(..)
618 break;
619 case SMPPairingState::FAILED: {
620 // next: deviceReady() or deviceDisconnected(..)
621 } break;
622 case SMPPairingState::REQUESTED_BY_RESPONDER:
623 // next: FEATURE_EXCHANGE_STARTED
624 break;
625 case SMPPairingState::FEATURE_EXCHANGE_STARTED:
626 // next: FEATURE_EXCHANGE_COMPLETED
627 break;
628 case SMPPairingState::FEATURE_EXCHANGE_COMPLETED:
629 // next: PASSKEY_EXPECTED... or KEY_DISTRIBUTION
630 break;
631 case SMPPairingState::PASSKEY_EXPECTED: {
632 const BTSecurityRegistry::Entry* sec = BTSecurityRegistry::getStartOf(device->getAddressAndType().address, "");
633 if( nullptr != sec && sec->getPairingPasskey() != BTSecurityRegistry::Entry::NO_PASSKEY ) {
634 std::thread dc(&BTDevice::setPairingPasskey, device, static_cast<uint32_t>( sec->getPairingPasskey() ));
635 dc.detach();
636 } else {
637 std::thread dc(&BTDevice::setPairingPasskey, device, 0);
638 // 3s disconnect: std::thread dc(&BTDevice::setPairingPasskeyNegative, device);
639 dc.detach();
640 }
641 // next: KEY_DISTRIBUTION or FAILED
642 } break;
643 case SMPPairingState::NUMERIC_COMPARE_EXPECTED: {
644 const BTSecurityRegistry::Entry* sec = BTSecurityRegistry::getStartOf(device->getAddressAndType().address, "");
645 if( nullptr != sec ) {
646 std::thread dc(&BTDevice::setPairingNumericComparison, device, sec->getPairingNumericComparison());
647 dc.detach();
648 } else {
649 std::thread dc(&BTDevice::setPairingNumericComparison, device, false);
650 dc.detach();
651 }
652 // next: KEY_DISTRIBUTION or FAILED
653 } break;
654 case SMPPairingState::OOB_EXPECTED:
655 // FIXME: ABORT
656 break;
657 case SMPPairingState::KEY_DISTRIBUTION:
658 // next: COMPLETED or FAILED
659 break;
660 case SMPPairingState::COMPLETED:
661 // next: deviceReady(..)
662 break;
663 default: // nop
664 break;
665 }
666 }
667
668 void deviceReady(const BTDeviceRef& device, const uint64_t timestamp) override {
669 (void)timestamp;
670 {
673 }
674 fprintf_td(stderr, "****** To Client: READY-0: Processing %s\n", device->toString(true).c_str());
675 }
676
677 void deviceDisconnected(const BTDeviceRef& device, const HCIStatusCode reason, const uint16_t handle, const uint64_t timestamp) override {
679 fprintf_td(stderr, "****** DISCONNECTED (count %zu): Reason 0x%X (%s), old handle %s: %s\n",
680 servedClientConnections.load(), static_cast<uint8_t>(reason), to_string(reason).c_str(),
681 to_hexstring(handle).c_str(), device->toString(true).c_str());
682
683 {
685 connectedDeviceToClient = nullptr;
686 }
687 std::thread sd(::processDisconnectedDeviceToClient, device); // @suppress("Invalid arguments")
688 sd.detach();
689 (void)timestamp;
690 }
691
692 std::string toString() const noexcept override {
693 return "MyAdapterServerStatusListener[this "+to_hexstring(this)+"]";
694 }
695
696};
697
698static void processDisconnectedDeviceToClient(BTDeviceRef device) { // NOLINT(performance-unnecessary-value-param): Pass-by-value out-of-thread
699 fprintf_td(stderr, "****** To Client: Disconnected Device (count %zu): Start %s\n",
700 servedClientConnections.load(), device->toString().c_str());
701
702 // already unpaired
703 stopAdvertisingToClient(adapterToClient, "processDisconnectedDeviceToClient");
704
705 jau::sleep_for( 100_ms ); // wait a little (FIXME: Fast restart of advertising error)
706
707 BTDeviceRef devToServer;
708 {
710 devToServer = connectedDeviceToServer;
711 }
712 if( nullptr != devToServer ) {
713 std::thread sd(::resetConnectionToServer, devToServer); // @suppress("Invalid arguments")
714 sd.detach();
715 } else {
716 startAdvertisingToClient(adapterToClient, "processDisconnectedDeviceToClient");
717 }
718
719 fprintf_td(stderr, "****** To Client: Disonnected Device: End %s\n", device->toString().c_str());
720}
721
722static bool startAdvertisingToClient(const BTAdapterRef& a, const std::string& msg) {
723 BTDeviceRef devToServer;
724 {
726 devToServer = connectedDeviceToServer;
727 }
728 if( nullptr == devToServer ) {
729 fprintf_td(stderr, "To Client: Start advertising: Skipped, not connected to server\n");
730 return false;
731 }
732
733 EInfoReport eir = *devToServer->getEIR();
734 const EIRDataType ind_mask = EIR_DATA_TYPE_MASK & devToServer->getEIRInd()->getEIRDataMask();
735 const EIRDataType scanrsp_mask = EIR_DATA_TYPE_MASK & devToServer->getEIRScanRsp()->getEIRDataMask();
736
737 DBGattServerRef dbGattServer( new DBGattServer( devToServer ) );
738 fprintf_td(stderr, "To Client: Start advertising: GattServer %s\n", dbGattServer->toString().c_str());
739
742 if( nullptr != gattDevNameChar ) {
743 std::string aname = a->getName();
744 gattDevNameChar->setValue(reinterpret_cast<uint8_t*>(aname.data()), aname.size(), 0);
745 }
746
747 fprintf_td(stderr, "****** To Client: Start advertising (%s): EIR %s\n", msg.c_str(), eir.toString().c_str());
748 fprintf_td(stderr, "****** To Client: Start advertising (%s): adv %s, scanrsp %s\n", msg.c_str(), to_string(ind_mask).c_str(), to_string(scanrsp_mask).c_str());
749
750 HCIStatusCode status = a->startAdvertising(dbGattServer, eir, ind_mask, scanrsp_mask,
753 fprintf_td(stderr, "****** To Client: Start advertising (%s) result: %s: %s\n", msg.c_str(), to_string(status).c_str(), a->toString().c_str());
754 fprintf_td(stderr, "%s", dbGattServer->toFullString().c_str());
755 return HCIStatusCode::SUCCESS == status;
756}
757
758static bool stopAdvertisingToClient(const BTAdapterRef& a, const std::string& msg) {
759 HCIStatusCode status = a->stopAdvertising();
760 fprintf_td(stderr, "****** To Client: Stop advertising (%s) result: %s: %s\n", msg.c_str(), to_string(status).c_str(), a->toString().c_str());
761 return HCIStatusCode::SUCCESS == status;
762}
763
764static bool initAdapterToClient(std::shared_ptr<BTAdapter>& adapter) {
765 if( adapterToClientAddr != EUI48::ALL_DEVICE && adapterToClientAddr != adapter->getAddressAndType().address ) {
766 fprintf_td(stderr, "initAdapterToClient: Adapter not selected: %s\n", adapter->toString().c_str());
767 return false;
768 }
769 if( !adapter->isInitialized() ) {
770 // Initialize with defaults and power-on
771 const HCIStatusCode status = adapter->initialize( btMode, false );
772 if( HCIStatusCode::SUCCESS != status ) {
773 fprintf_td(stderr, "initAdapterToClient: initialize failed: %s: %s\n",
774 to_string(status).c_str(), adapter->toString().c_str());
775 return false;
776 }
777 } else if( !adapter->setPowered( false ) ) {
778 fprintf_td(stderr, "initAdapterToClient: setPower.1 off failed: %s\n", adapter->toString().c_str());
779 return false;
780 }
781 // adapter is powered-off
782 fprintf_td(stderr, "initAdapterToClient.1: %s\n", adapter->toString().c_str());
783
784 {
786 if( HCIStatusCode::SUCCESS == status ) {
787 fprintf_td(stderr, "initAdapterToClient: setLocalName OK: %s\n", adapter->toString().c_str());
788 } else {
789 fprintf_td(stderr, "initAdapterToClient: setLocalName failed: %s\n", adapter->toString().c_str());
790 return false;
791 }
792
793 status = adapter->setSecureConnections( adapterToClientUseSC );
794 if( HCIStatusCode::SUCCESS == status ) {
795 fprintf_td(stderr, "initAdapterToClient: setSecureConnections OK: %s\n", adapter->toString().c_str());
796 } else {
797 fprintf_td(stderr, "initAdapterToClient: setSecureConnections failed: %s\n", adapter->toString().c_str());
798 return false;
799 }
800
801 const uint16_t conn_min_interval = 8; // 10ms
802 const uint16_t conn_max_interval = 40; // 50ms
803 const uint16_t conn_latency = 0;
804 const uint16_t supervision_timeout = 50; // 500ms
805 status = adapter->setDefaultConnParam(conn_min_interval, conn_max_interval, conn_latency, supervision_timeout);
806 if( HCIStatusCode::SUCCESS == status ) {
807 fprintf_td(stderr, "initAdapterToClient: setDefaultConnParam OK: %s\n", adapter->toString().c_str());
808 } else if( HCIStatusCode::UNKNOWN_COMMAND == status ) {
809 fprintf_td(stderr, "initAdapterToClient: setDefaultConnParam UNKNOWN_COMMAND (ignored): %s\n", adapter->toString().c_str());
810 } else {
811 fprintf_td(stderr, "initAdapterToClient: setDefaultConnParam failed: %s, %s\n", to_string(status).c_str(), adapter->toString().c_str());
812 return false;
813 }
814
815 if( !adapter->setPowered( true ) ) {
816 fprintf_td(stderr, "initAdapterToClient: setPower.2 on failed: %s\n", adapter->toString().c_str());
817 return false;
818 }
819 }
820 fprintf_td(stderr, "initAdapterToClient.2: %s\n", adapter->toString().c_str());
821
822 {
823 const LE_Features le_feats = adapter->getLEFeatures();
824 fprintf_td(stderr, "initAdapterToClient: LE_Features %s\n", to_string(le_feats).c_str());
825 }
826 if( adapter->getBTMajorVersion() > 4 ) {
827 LE_PHYs Tx { LE_PHYs::LE_2M }, Rx { LE_PHYs::LE_2M };
828 HCIStatusCode res = adapter->setDefaultLE_PHY(Tx, Rx);
829 fprintf_td(stderr, "initAdapterToClient: Set Default LE PHY: status %s: Tx %s, Rx %s\n",
830 to_string(res).c_str(), to_string(Tx).c_str(), to_string(Rx).c_str());
831 }
832 adapter->setSMPKeyPath(SERVER_KEY_PATH);
833
834 std::shared_ptr<AdapterStatusListener> asl( std::make_shared<AdapterToClientStatusListener>() );
835 adapter->addStatusListener( asl );
836
837 adapter->setServerConnSecurity(adapterToClientSecLevel, SMPIOCapability::UNSET);
838
839 return true;
840}
841
842//
843// Common: To Server and Client
844//
845
846static void myChangedAdapterSetFunc(const bool added, std::shared_ptr<BTAdapter>& adapter) {
847 if( added ) {
848 if( nullptr == adapterToServer ) {
849 if( initAdapterToServer( adapter ) ) {
850 adapterToServer = adapter;
851 fprintf_td(stderr, "****** AdapterToServer ADDED__: InitOK: %s\n", adapter->toString().c_str());
852 return;
853 }
854 }
855 if( nullptr == adapterToClient ) {
856 if( initAdapterToClient( adapter ) ) {
857 adapterToClient = adapter;
858 fprintf_td(stderr, "****** AdapterToClient ADDED__: InitOK: %s\n", adapter->toString().c_str());
859 return;
860 }
861 }
862 fprintf_td(stderr, "****** Adapter ADDED__: Ignored: %s\n", adapter->toString().c_str());
863 } else {
864 if( nullptr != adapterToServer && adapter == adapterToServer ) {
865 adapterToServer = nullptr;
866 fprintf_td(stderr, "****** AdapterToServer REMOVED: %s\n", adapter->toString().c_str());
867 return;
868 }
869 if( nullptr != adapterToClient && adapter == adapterToClient ) {
870 adapterToClient = nullptr;
871 fprintf_td(stderr, "****** AdapterToClient REMOVED: %s\n", adapter->toString().c_str());
872 return;
873 }
874 fprintf_td(stderr, "****** Adapter REMOVED: Ignored %s\n", adapter->toString().c_str());
875 }
876}
877
878void test() {
880
881 std::shared_ptr<BTManager> mngr = BTManager::get();
882 mngr->addChangedAdapterSetCallback(myChangedAdapterSetFunc);
883
885 jau::sleep_for( 2_s );
886 }
887 adapterToServer = nullptr;
888 adapterToClient = nullptr;
889
890 //
891 // just a manually controlled pull down to show status, not required
892 //
893 jau::darray<std::shared_ptr<BTAdapter>> adapterList = mngr->getAdapters();
894
895 jau::for_each_const(adapterList, [](const std::shared_ptr<BTAdapter>& adapter) {
896 fprintf_td(stderr, "****** EOL Adapter's Devices - pre close: %s\n", adapter->toString().c_str());
897 adapter->printDeviceLists();
898 });
899 {
900 BTManager::size_type count = mngr->removeChangedAdapterSetCallback(myChangedAdapterSetFunc);
901 fprintf_td(stderr, "****** EOL Removed ChangedAdapterSetCallback %zu\n", (size_t)count);
902
903 mngr->close();
904 }
905 jau::for_each_const(adapterList, [](const std::shared_ptr<BTAdapter>& adapter) {
906 fprintf_td(stderr, "****** EOL Adapter's Devices - post close: %s\n", adapter->toString().c_str());
907 adapter->printDeviceLists();
908 });
909}
910
911#include <cstdio>
912
913int main(int argc, char *argv[])
914{
915 bool waitForEnter=false;
916
917 fprintf_td(stderr, "Direct-BT Native Version %s (API %s)\n", DIRECT_BT_VERSION, DIRECT_BT_VERSION_API);
918
919 for(int i=1; i<argc; i++) {
920 fprintf(stderr, "arg[%d/%d]: '%s'\n", i, argc, argv[i]);
921
922 if( !strcmp("-dbt_debug", argv[i]) && argc > (i+1) ) {
923 setenv("direct_bt.debug", argv[++i], 1 /* overwrite */);
924 } else if( !strcmp("-dbt_verbose", argv[i]) && argc > (i+1) ) {
925 setenv("direct_bt.verbose", argv[++i], 1 /* overwrite */);
926 } else if( !strcmp("-dbt_gatt", argv[i]) && argc > (i+1) ) {
927 setenv("direct_bt.gatt", argv[++i], 1 /* overwrite */);
928 } else if( !strcmp("-dbt_l2cap", argv[i]) && argc > (i+1) ) {
929 setenv("direct_bt.l2cap", argv[++i], 1 /* overwrite */);
930 } else if( !strcmp("-dbt_hci", argv[i]) && argc > (i+1) ) {
931 setenv("direct_bt.hci", argv[++i], 1 /* overwrite */);
932 } else if( !strcmp("-dbt_mgmt", argv[i]) && argc > (i+1) ) {
933 setenv("direct_bt.mgmt", argv[++i], 1 /* overwrite */);
934 } else if( !strcmp("-wait", argv[i]) ) {
935 waitForEnter = true;
936 } else if( !strcmp("-quiet", argv[i]) ) {
937 QUIET = true;
938 } else if( !strcmp("-discoveryPolicy", argv[i]) ) {
939 discoveryPolicy = to_DiscoveryPolicy(atoi(argv[++i]));
940 } else if( !strcmp("-btmode", argv[i]) && argc > (i+1) ) {
941 btMode = to_BTMode(argv[++i]);
942 } else if( !strcmp("-use_sc", argv[i]) && argc > (i+1) ) {
943 adapterToClientUseSC = 0 != atoi(argv[++i]);
944 } else if( !strcmp("-adapterToClient", argv[i]) && argc > (i+1) ) {
945 adapterToClientAddr = EUI48( std::string(argv[++i]) );
946 } else if( !strcmp("-nameToClient", argv[i]) && argc > (i+1) ) {
947 adapterToClientName = std::string(argv[++i]);
948 } else if( !strcmp("-mtuToClient", argv[i]) && argc > (i+1) ) {
949 max_att_mtu_to_client = atoi(argv[++i]);
950 } else if( !strcmp("-seclevelToClient", argv[i]) && argc > (i+1) ) {
952 fprintf(stderr, "Set sec_level 2 client %s\n", to_string(adapterToClientSecLevel).c_str());
953 } else if( !strcmp("-adapterToServer", argv[i]) && argc > (i+1) ) {
954 adapterToServerAddr = EUI48( std::string(argv[++i]) );
955 } else if( !strcmp("-server", argv[i]) && argc > (i+1) ) {
956 std::string addrOrNameSub = std::string(argv[++i]);
958 } else if( !strcmp("-passkeyToServer", argv[i]) && argc > (i+2) ) {
959 const std::string addrOrNameSub(argv[++i]);
961 sec->passkey = atoi(argv[++i]);
962 fprintf(stderr, "Set passkey to server in %s\n", sec->toString().c_str());
963 } else if( !strcmp("-seclevelToServer", argv[i]) && argc > (i+2) ) {
964 const std::string addrOrNameSub(argv[++i]);
966 sec->sec_level = to_BTSecurityLevel(atoi(argv[++i]));
967 fprintf(stderr, "Set sec_level to server in %s\n", sec->toString().c_str());
968 } else if( !strcmp("-iocapToServer", argv[i]) && argc > (i+2) ) {
969 const std::string addrOrNameSub(argv[++i]);
971 sec->io_cap = to_SMPIOCapability(atoi(argv[++i]));
972 fprintf(stderr, "Set io_cap to server in %s\n", sec->toString().c_str());
973 } else if( !strcmp("-secautoToServer", argv[i]) && argc > (i+2) ) {
974 const std::string addrOrNameSub(argv[++i]);
976 sec->io_cap_auto = to_SMPIOCapability(atoi(argv[++i]));
977 fprintf(stderr, "Set SEC AUTO security io_cap to server in %s\n", sec->toString().c_str());
978 } else if( !strcmp("-count", argv[i]) && argc > (i+1) ) {
979 MAX_SERVED_CONNECTIONS = atoi(argv[++i]);
980 }
981 }
982 fprintf_td(stderr, "pid %d\n", getpid());
983
984 fprintf_td(stderr, "Run with '[-btmode LE|BREDR|DUAL] [-use_sc 0|1] [-count <connection_number>] [-quiet] "
985 "[-discoveryPolicy <0-4>] "
986 "[-adapterToClient <adapter_address>] "
987 "[-nameToClient <adapter_name>] "
988 "[-mtuToClient <max att_mtu>] "
989 "[-seclevelToClient <int_sec_level>]* "
990 "[-adapterToServer <adapter_address>] "
991 "(-server <device_[address|name]_sub>)* "
992 "(-seclevelToServer <device_[address|name]_sub> <int_sec_level>)* "
993 "(-iocapToServer <device_[address|name]_sub> <int_iocap>)* "
994 "(-secautoToServer <device_[address|name]_sub> <int_iocap>)* "
995 "(-passkeyToServer <device_[address|name]_sub> <digits>)* "
996 "[-dbt_verbose true|false] "
997 "[-dbt_debug true|false|adapter.event,gatt.data,hci.event,hci.scan_ad_eir,mgmt.event] "
998 "[-dbt_mgmt cmd.timeout=3000,ringsize=64,...] "
999 "[-dbt_hci cmd.complete.timeout=10000,cmd.status.timeout=3000,ringsize=64,...] "
1000 "[-dbt_gatt cmd.read.timeout=500,cmd.write.timeout=500,cmd.init.timeout=2500,ringsize=128,...] "
1001 "[-dbt_l2cap reader.timeout=10000,restart.count=0,...] "
1002 "\n");
1003
1004 fprintf_td(stderr, "btmode %s\n", to_string(btMode).c_str());
1005 fprintf_td(stderr, "MAX_SERVED_CONNECTIONS %d\n", MAX_SERVED_CONNECTIONS);
1006 fprintf_td(stderr, "To Client Settings (acting as server):\n");
1007 fprintf_td(stderr, "- adapter %s\n", adapterToClientAddr.toString().c_str());
1008 fprintf_td(stderr, "- SC %s\n", to_string(adapterToClientUseSC).c_str());
1009 fprintf_td(stderr, "- name %s (short %s)\n", adapterToClientName.c_str(), adapterToClientShortName.c_str());
1010 fprintf_td(stderr, "- mtu %d\n", (int)max_att_mtu_to_client);
1011 fprintf_td(stderr, "- sec_level %s\n", to_string(adapterToClientSecLevel).c_str());
1012 fprintf_td(stderr, "To Server Settings (acting as client):\n");
1013 fprintf_td(stderr, "- adapter %s\n", adapterToServerAddr.toString().c_str());
1014 fprintf_td(stderr, "- discoveryPolicy %s\n", to_string(discoveryPolicy).c_str());
1015 fprintf_td(stderr, "- security-details client: %s\n", BTSecurityRegistry::allToString().c_str());
1016 fprintf_td(stderr, "- server to connect to: %s\n", BTDeviceRegistry::getWaitForDevicesString().c_str());
1017
1018
1019 if( waitForEnter ) {
1020 fprintf_td(stderr, "Press ENTER to continue\n");
1021 getchar();
1022 }
1023 fprintf_td(stderr, "****** TEST start\n");
1024 test();
1025 fprintf_td(stderr, "****** TEST end\n");
1026 if( true ) {
1027 // Just for testing purpose, i.e. triggering BTManager::close() within the test controlled app,
1028 // instead of program shutdown.
1029 fprintf_td(stderr, "****** Manager close start\n");
1030 std::shared_ptr<BTManager> mngr = BTManager::get(); // already existing
1031 mngr->close();
1032 fprintf_td(stderr, "****** Manager close end\n");
1033 }
1034}
void mtuResponse(const uint16_t clientMTU, const AttPDUMsg &pduReply, const AttErrorRsp::ErrorCode error_reply, const uint16_t serverMTU, const uint16_t usedMTU, const BTDeviceRef &serverReplier, const BTDeviceRef &clientRequester) override
Informal notification about a complete MTU exchange request and response to and from this GATTRole::S...
void readResponse(const uint16_t handle, const uint16_t value_offset, const AttPDUMsg &pduReply, const AttErrorRsp::ErrorCode error_reply, const jau::TROOctets &data_reply, const BTDeviceRef &serverReplier, const BTDeviceRef &clientRequester) override
Informal notification about a complete read request and response to and from this GATTRole::Server,...
BTDeviceRef getToClient() noexcept
void writeResponse(const AttPDUMsg &pduReply, const AttErrorRsp::ErrorCode error_code, const BTDeviceRef &serverSource, const BTDeviceRef &clientDest) override
Informal notification about a write response received from this GATTRole::Server, optional.
void indicationReceived(const BTDeviceRef &source, const uint16_t char_handle, const TROOctets &char_value, const uint64_t timestamp, const bool confirmationSent) override
Called from native BLE stack, initiated by a received indication.
void writeRequest(const uint16_t handle, const jau::TROOctets &data, const jau::darray< Section > &sections, const bool with_response, const BTDeviceRef &serverDest, const BTDeviceRef &clientSource) override
Informal notification about a completed write request sent to this GATTRole::Server,...
void notificationReceived(const BTDeviceRef &source, const uint16_t char_handle, const TROOctets &char_value, const uint64_t timestamp) override
Called from native BLE stack, initiated by a received notification.
BTAdapter status listener for remote BTDevice discovery events: Added, updated and removed; as well a...
Definition: BTAdapter.hpp:127
Handles the Attribute Protocol (ATT) using Protocol Data Unit (PDU) encoded messages over L2CAP chann...
virtual std::string toString() const noexcept
BTAdapter represents one local Bluetooth Controller.
Definition: BTAdapter.hpp:324
std::string toString() const noexcept override
Definition: BTAdapter.hpp:1331
HCIStatusCode startDiscovery(const DBGattServerRef &gattServerData_=nullptr, const DiscoveryPolicy policy=DiscoveryPolicy::PAUSE_CONNECTED_UNTIL_READY, const bool le_scan_active=true, const uint16_t le_scan_interval=24, const uint16_t le_scan_window=24, const uint8_t filter_policy=0x00, const bool filter_dup=true) noexcept
Starts discovery.
Definition: BTAdapter.cpp:1150
BDAddressAndType const & getAddressAndType() const noexcept
Returns the adapter's public BDAddressAndType, i.e.
Definition: BTAdapter.hpp:691
bool setPowered(const bool power_on) noexcept
Set the power state of the adapter.
Definition: BTAdapter.cpp:660
Native GATT characteristic event listener for notification and indication events received from a GATT...
jau::nsize_t size_type
Definition: BTManager.hpp:211
Representing a complete list of Gatt Service objects from the GATTRole::Server perspective,...
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
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
Implementation of a dynamic linear array storage, aka vector.
Definition: darray.hpp:148
This class provides a RAII-style Sequentially Consistent (SC) data race free (DRF) critical block.
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")))))))
static const uint8_t filter_policy
int main(int argc, char *argv[])
static BTAdapterRef adapterToServer
static const uint16_t le_scan_interval
static uint64_t timestamp_t0
static EUI48 adapterToClientAddr
static const uint16_t adv_interval_max
static const uint16_t le_scan_window
static uint16_t max_att_mtu_to_client
static std::string adapterToClientShortName
static bool QUIET
static std::atomic< int > serverDeviceReadyCount
static jau::sc_atomic_bool sync_data
static void processReadyToServer(const BTDeviceRef &device)
static const uint8_t adv_chan_map
static void removeDeviceToServer(BTDeviceRef device)
static void myChangedAdapterSetFunc(const bool added, std::shared_ptr< BTAdapter > &adapter)
static EUI48 adapterToServerAddr
static std::string adapterToClientName
static bool adapterToClientUseSC
static BTDeviceRef connectedDeviceToServer
static bool initAdapterToClient(std::shared_ptr< BTAdapter > &adapter)
static BTDeviceRef connectedDeviceToClient
static DiscoveryPolicy discoveryPolicy
static void processDisconnectedDeviceToClient(BTDeviceRef device)
static BTSecurityLevel adapterToClientSecLevel
static bool initAdapterToServer(std::shared_ptr< BTAdapter > &adapter)
static const bool le_scan_active
static BTMode btMode
static void connectToDiscoveredServer(BTDeviceRef device)
static bool startDiscoveryToServer(BTAdapter *a, std::string msg)
static bool startAdvertisingToClient(const BTAdapterRef &a, const std::string &msg)
static BTAdapterRef adapterToClient
static jau::nsize_t MAX_SERVED_CONNECTIONS
static const uint16_t adv_interval_min
static const AD_PDU_Type adv_type
void test()
static bool stopAdvertisingToClient(const BTAdapterRef &a, const std::string &msg)
static void resetConnectionToServer(BTDeviceRef device)
static jau::relaxed_atomic_nsize_t servedClientConnections
constexpr const char SERVER_KEY_PATH[]
constexpr const char CLIENT_KEY_PATH[]
C++20 we could use constexpr std::string
constexpr UnaryFunction for_each_const(T &data, UnaryFunction f, std::enable_if_t< is_cow_type< T >::value, bool >=true) noexcept
uint32_t dfa_utf8_decode(uint32_t &state, uint32_t &codep, const uint32_t byte_value)
std::string to_string(const alphabet &v) noexcept
Definition: base_codec.hpp:97
SMPPairingState
SMP Pairing Process state definition.
Definition: SMPTypes.hpp:107
constexpr SMPIOCapability to_SMPIOCapability(const uint8_t v) noexcept
Definition: SMPTypes.hpp:223
constexpr const EIRDataType EIR_DATA_TYPE_MASK
Explicit mask to erase all implicit set EIRDataType fields: EVT_TYPE, EXT_EVT_TYPE,...
Definition: BTTypes0.hpp:881
Entry * getStartOf(const EUI48 &addr, const std::string &name) noexcept
Returns a matching Entry,.
constexpr BTSecurityLevel to_BTSecurityLevel(const uint8_t v) noexcept
Definition: BTTypes0.hpp:300
Entry * getOrCreate(const std::string &addrOrNameSub) noexcept
Determines whether the given addrOrNameSub is a EUI48Sub or just a name and retrieves an entry.
BTMode
Bluetooth adapter operating mode.
Definition: BTTypes0.hpp:112
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
Entry * get(const EUI48 &addr, const std::string &name, AddressNameEntryMatchFunc m) noexcept
Returns a matching BTSecurityRegistry::Entry with the given addr and/or name.
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
BTMode to_BTMode(const std::string &value) noexcept
Maps the specified name to a constant of BTMode.
Definition: BTTypes0.cpp:226
std::string allToString() noexcept
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
constexpr DiscoveryPolicy to_DiscoveryPolicy(const uint8_t v) noexcept
Definition: BTAdapter.hpp:103
constexpr uint16_t getHCIConnSupervisorTimeout(const uint16_t conn_latency, const uint16_t conn_interval_max_ms, const uint16_t min_result_ms=number(HCIConstInt::LE_CONN_MIN_TIMEOUT_MS), const uint16_t multiplier=10) noexcept
Defining the supervising timeout for LE connections to be a multiple of the maximum connection interv...
Definition: HCITypes.hpp:102
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< DBGattServer > DBGattServerRef
std::shared_ptr< DBGattChar > DBGattCharRef
bool remove(const std::string &path, const traverse_options topts=traverse_options::none) noexcept
Remove the given path.
Definition: file_util.cpp:1173
void sync() noexcept
Synchronizes filesystems, i.e.
Definition: file_util.cpp:1932
uint_fast32_t nsize_t
Natural 'size_t' alternative using uint_fast32_t as its natural sized type.
Definition: int_types.hpp:53
std::string to_hexstring(value_type const &v) noexcept
Produce a lower-case hexadecimal string representation of the given pointer.
void addToWaitForDevices(const std::string &addrOrNameSub) noexcept
std::string getWaitForDevicesString() noexcept
bool isWaitingForAnyDevice() noexcept
void addToProcessedDevices(const BDAddressAndType &a, const std::string &n) noexcept
bool isWaitingForDevice(const EUI48 &address, const std::string &name, DeviceQueryMatchFunc m) noexcept
Returns true if the given address and/or name matches any of the BTDeviceRegistry::addToWaitForDevice...
__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
uint64_t getCurrentMilliseconds() noexcept
Returns current monotonic time in milliseconds.
Definition: basic_types.cpp:64
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 bool isSecLevelOrIOCapSet() const noexcept
constexpr const BTSecurityLevel & getSecLevel() const noexcept
constexpr int getPairingPasskey() const noexcept
constexpr bool getPairingNumericComparison() const noexcept
constexpr const SMPIOCapability & getSecurityAutoIOCap() const noexcept
constexpr const SMPIOCapability & getIOCap() const noexcept
constexpr bool isSecurityAutoEnabled() const noexcept
std::string toString() 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
std::string toString() const noexcept
Definition: eui48.cpp:167
CXX_ALWAYS_INLINE _Tp load() const noexcept
@ GENERIC_ACCESS
This service contains generic information about the device.
@ DEVICE_NAME