36 #include <sys/socket.h>
50 #include "L2CAPIoctl.hpp"
63 BTGattEnv::BTGattEnv() noexcept
64 : exploding(
jau::environment::getExplodingProperties("
direct_bt.gatt") ),
65 GATT_READ_COMMAND_REPLY_TIMEOUT(
jau::environment::getInt32Property("
direct_bt.gatt.cmd.read.timeout", 500, 250 , INT32_MAX ) ),
66 GATT_WRITE_COMMAND_REPLY_TIMEOUT(
jau::environment::getInt32Property("
direct_bt.gatt.cmd.write.timeout", 500, 250 , INT32_MAX ) ),
67 GATT_INITIAL_COMMAND_REPLY_TIMEOUT(
jau::environment::getInt32Property("
direct_bt.gatt.cmd.init.timeout", 2500, 2000 , INT32_MAX ) ),
68 ATTPDU_RING_CAPACITY(
jau::environment::getInt32Property("
direct_bt.gatt.ringsize", 128, 64 , 1024 ) ),
69 DEBUG_DATA(
jau::environment::getBooleanProperty("
direct_bt.debug.gatt.data", false) )
73 #define CASE_TO_STRING(V) case V: return #V;
76 std::shared_ptr<BTDevice> ref = wbr_device.lock();
77 if(
nullptr == ref ) {
83 bool BTGattHandler::validateConnected() noexcept {
84 bool l2capIsConnected = l2cap.
isOpen();
87 if( has_ioerror || l2capHasIOError ) {
89 ERR_PRINT(
"IOError state: GattHandler %s, l2cap %s: %s",
94 if( !is_connected || !l2capIsConnected ) {
95 ERR_PRINT(
"Disconnected state: GattHandler %s, l2cap %s: %s",
103 [](
const std::shared_ptr<BTGattCharListener> &a,
const std::shared_ptr<BTGattCharListener> &b) ->
bool {
return *a == *b; };
114 ERR_PRINT(
"Given GATTCharacteristicListener ref is null");
123 ERR_PRINT(
"Given GATTCharacteristicListener ref is null");
126 auto it = characteristicListenerList.begin();
127 for (; !it.is_end(); ++it ) {
138 if(
nullptr == associatedCharacteristic ) {
139 ERR_PRINT(
"Given GATTCharacteristic ref is null");
142 return removeAllAssociatedCharListener( associatedCharacteristic.get() );
146 if(
nullptr == associatedCharacteristic ) {
147 ERR_PRINT(
"Given GATTCharacteristic ref is null");
151 auto it = characteristicListenerList.begin();
152 while( !it.is_end() ) {
153 if ( (*it)->match(*associatedCharacteristic) ) {
167 int count = characteristicListenerList.
size();
168 characteristicListenerList.
clear();
173 sendIndicationConfirmation = v;
177 return sendIndicationConfirmation;
180 void BTGattHandler::l2capReaderThreadImpl() {
182 const std::lock_guard<std::mutex> lock(mtx_l2capReaderLifecycle);
183 l2capReaderShallStop =
false;
184 l2capReaderRunning =
true;
185 DBG_PRINT(
"GATTHandler::reader Started");
186 cv_l2capReaderInit.notify_all();
189 DBG_PRINT(
"GATTHandler::l2capReaderThreadCleanup: l2capReaderRunning %d -> 0", l2capReaderRunning.
load());
190 l2capReaderRunning =
false;
193 while( !l2capReaderShallStop ) {
195 if( !validateConnected() ) {
196 ERR_PRINT(
"GATTHandler::reader: Invalid IO state -> Stop");
197 l2capReaderShallStop =
true;
217 if( l->
match(*decl) ) {
218 l->notificationReceived(decl, data_view, timestamp);
220 }
catch (std::exception &e) {
221 ERR_PRINT(
"GATTHandler::notificationReceived-CBs %d/%zd: GATTCharacteristicListener %s: Caught exception %s",
222 i+1, characteristicListenerList.
size(),
229 COND_PRINT(env.
DEBUG_DATA,
"GATTHandler::reader: IND: %s, sendIndicationConfirmation %d, listener %zd",
230 a->
toString().c_str(), sendIndicationConfirmation.
load(), characteristicListenerList.
size());
231 bool cfmSent =
false;
232 if( sendIndicationConfirmation ) {
245 if( l->
match(*decl) ) {
246 l->indicationReceived(decl, data_view, timestamp, cfmSent);
248 }
catch (std::exception &e) {
249 ERR_PRINT(
"GATTHandler::indicationReceived-CBs %d/%zd: GATTCharacteristicListener %s, cfmSent %d: Caught exception %s",
250 i+1, characteristicListenerList.
size(),
257 ERR_PRINT(
"GATTHandler::reader: MULTI-NTF not implemented: %s", attPDU->
toString().c_str());
259 attPDURing.putBlocking( std::move(attPDU) );
261 }
else if( ETIMEDOUT != errno && !l2capReaderShallStop ) {
262 IRQ_PRINT(
"GATTHandler::reader: l2cap read error -> Stop; l2cap.read %d", len);
263 l2capReaderShallStop =
true;
268 const std::lock_guard<std::mutex> lock(mtx_l2capReaderLifecycle);
269 WORDY_PRINT(
"GATTHandler::reader: Ended. Ring has %u entries flushed", attPDURing.getSize());
271 l2capReaderRunning =
false;
272 cv_l2capReaderInit.notify_all();
281 deviceString(device->getAddressAndType().toString()),
282 rbuffer(
number(Defaults::MAX_ATT_MTU)),
283 is_connected(l2cap.isOpen()), has_ioerror(
false),
284 attPDURing(
nullptr, env.ATTPDU_RING_CAPACITY), l2capReaderShallStop(
false),
285 l2capReaderThreadId(0), l2capReaderRunning(
false),
286 serverMTU(
number(Defaults::MIN_ATT_MTU)), usedMTU(
number(Defaults::MIN_ATT_MTU))
288 if( !validateConnected() ) {
289 ERR_PRINT(
"GATTHandler.ctor: L2CAP could not connect");
290 is_connected =
false;
293 DBG_PRINT(
"GATTHandler::ctor: Start Connect: GattHandler[%s], l2cap[%s]: %s",
294 getStateString().c_str(), l2cap.getStateString().c_str(), deviceString.c_str());
301 std::unique_lock<std::mutex> lock(mtx_l2capReaderLifecycle);
303 std::thread l2capReaderThread(&BTGattHandler::l2capReaderThreadImpl,
this);
304 l2capReaderThreadId = l2capReaderThread.native_handle();
307 l2capReaderThread.detach();
309 while(
false == l2capReaderRunning ) {
310 cv_l2capReaderInit.wait(lock);
317 mtu = exchangeMTUImpl(
number(Defaults::MAX_ATT_MTU), env.GATT_INITIAL_COMMAND_REPLY_TIMEOUT);
318 }
catch (std::exception &e) {
319 ERR_PRINT2(
"GattHandler.ctor: exchangeMTU failed: %s", e.what());
320 }
catch (std::string &msg) {
321 ERR_PRINT2(
"GattHandler.ctor: exchangeMTU failed: %s", msg.c_str());
322 }
catch (
const char *msg) {
323 ERR_PRINT2(
"GattHandler.ctor: exchangeMTU failed: %s", msg);
326 ERR_PRINT2(
"GATTHandler::ctor: Zero serverMTU -> disconnect: %s", deviceString.c_str());
327 disconnect(
true ,
false );
330 usedMTU = std::min(
number(Defaults::MAX_ATT_MTU), (
int)serverMTU);
336 characteristicListenerList.
clear();
338 genericAccess =
nullptr;
349 if( !is_connected.compare_exchange_strong(expConn,
false) ) {
351 DBG_PRINT(
"GATTHandler::disconnect: Not connected: disconnectDevice %d, ioErrorCause %d: GattHandler[%s], l2cap[%s]: %s",
352 disconnectDevice, ioErrorCause, getStateString().c_str(), l2cap.getStateString().c_str(), deviceString.c_str());
353 characteristicListenerList.clear();
357 const std::lock_guard<std::recursive_mutex> lock(mtx_command);
358 DBG_PRINT(
"GATTHandler::disconnect: Start: disconnectDevice %d, ioErrorCause %d: GattHandler[%s], l2cap[%s]: %s",
359 disconnectDevice, ioErrorCause, getStateString().c_str(), l2cap.getStateString().c_str(), deviceString.c_str());
360 characteristicListenerList.clear();
364 std::unique_lock<std::mutex> lockReader(mtx_l2capReaderLifecycle);
367 const pthread_t tid_self = pthread_self();
368 const pthread_t tid_l2capReader = l2capReaderThreadId;
369 l2capReaderThreadId = 0;
370 const bool is_l2capReader = tid_l2capReader == tid_self;
371 DBG_PRINT(
"GATTHandler.disconnect: l2capReader[running %d, shallStop %d, isReader %d, tid %p)",
372 l2capReaderRunning.load(), l2capReaderShallStop.load(), is_l2capReader, (
void*)tid_l2capReader);
373 if( l2capReaderRunning ) {
374 l2capReaderShallStop =
true;
375 if( !is_l2capReader && 0 != tid_l2capReader ) {
377 if( 0 != ( kerr = pthread_kill(tid_l2capReader, SIGALRM) ) ) {
378 ERR_PRINT(
"GATTHandler::disconnect: pthread_kill %p FAILED: %d", (
void*)tid_l2capReader, kerr);
382 while(
true == l2capReaderRunning ) {
383 cv_l2capReaderInit.wait(lockReader);
389 if( disconnectDevice ) {
390 std::shared_ptr<BTDevice> device = getDeviceUnchecked();
391 if(
nullptr != device ) {
402 DBG_PRINT(
"GATTHandler::disconnect: End: %s", deviceString.c_str());
406 void BTGattHandler::send(
const AttPDUMsg & msg) {
407 if( !validateConnected() ) {
418 IRQ_PRINT(
"GATTHandler::send: l2cap write error -> disconnect: %s to %s", msg.
toString().c_str(), deviceString.c_str());
423 if(
static_cast<size_t>(res) != msg.
pdu.
getSize() ) {
424 ERR_PRINT(
"GATTHandler::send: l2cap write count error, %zd != %zu: %s -> disconnect: %s",
433 std::unique_ptr<const AttPDUMsg> BTGattHandler::sendWithReply(
const AttPDUMsg & msg,
const int timeout) {
437 std::unique_ptr<const AttPDUMsg> res = attPDURing.getBlocking(timeout);
438 if(
nullptr == res ) {
440 IRQ_PRINT(
"GATTHandler::sendWithReply: nullptr result (timeout %d): req %s to %s", timeout, msg.
toString().c_str(), deviceString.c_str());
448 uint16_t BTGattHandler::exchangeMTUImpl(
const uint16_t clientMaxMTU,
const int32_t timeout) {
460 DBG_PRINT(
"GATT MTU send: %s to %s", req.toString().c_str(), deviceString.c_str());
462 std::unique_ptr<const AttPDUMsg> pdu = sendWithReply(req, timeout);
467 DBG_PRINT(
"GATT MTU recv: %u, %s from %s", mtu, pdu->
toString().c_str(), deviceString.c_str());
477 DBG_PRINT(
"GATT MTU handled error -> ATT_MTU %u, %s from %s", mtu, pdu->
toString().c_str(), deviceString.c_str());
479 WORDY_PRINT(
"GATT MTU unexpected error %s; req %s from %s", pdu->
toString().c_str(), req.toString().c_str(), deviceString.c_str());
482 ERR_PRINT(
"GATT MTU unexpected reply %s; req %s from %s", pdu->
toString().c_str(), req.toString().c_str(), deviceString.c_str());
490 return findCharacterisicsByValueHandle(charValueHandle, services);
494 for(
auto it = services_.begin(); it != services_.end(); it++) {
495 BTGattCharRef decl = findCharacterisicsByValueHandle(charValueHandle, *it);
496 if(
nullptr != decl ) {
504 for(
auto it = service->characteristicList.begin(); it != service->characteristicList.end(); it++) {
506 if( charValueHandle == decl->value_handle ) {
514 const std::lock_guard<std::recursive_mutex> lock(mtx_command);
518 for(
auto it = services.
begin(); it != services.
end(); it++) {
532 if( given_this !=
this ) {
545 const std::lock_guard<std::recursive_mutex> lock(mtx_command);
549 uint16_t startHandle=0x0001;
562 for(
int i=0; i<count; i++) {
571 count, result.
at(result.
size()-1)->toString().c_str(), deviceString.c_str());
574 if( startHandle < 0xffff ) {
582 ERR_PRINT(
"GATT discoverPrimary unexpected reply %s, req %s from %s",
589 return result.
size() > 0;
603 const std::lock_guard<std::recursive_mutex> lock(mtx_command);
604 COND_PRINT(env.
DEBUG_DATA,
"GATT discoverCharacteristics Service: %s on %s", service->toString().c_str(), deviceString.c_str());
609 uint16_t handle=service->startHandle;
610 service->characteristicList.clear();
612 const AttReadByNTypeReq req(
false , handle, service->endHandle, characteristicTypeReq);
622 for(
int e_iter=0; e_iter<e_count; e_iter++) {
635 service->characteristicList.at(service->characteristicList.size()-1)->toString().c_str(), deviceString.c_str());
638 if( handle < service->endHandle ) {
646 ERR_PRINT(
"GATT discoverCharacteristics unexpected reply %s, req %s within service%s from %s",
647 pdu->
toString().c_str(), req.
toString().c_str(), service->toString().c_str(), deviceString.c_str());
654 return service->characteristicList.size() > 0;
664 COND_PRINT(env.
DEBUG_DATA,
"GATT discoverDescriptors Service: %s on %s", service->toString().c_str(), deviceString.c_str());
665 const std::lock_guard<std::recursive_mutex> lock(mtx_command);
668 const int charCount = service->characteristicList.size();
669 for(
int charIter=0; charIter < charCount; charIter++ ) {
670 BTGattCharRef charDecl = service->characteristicList[charIter];
671 charDecl->clearDescriptors();
672 COND_PRINT(env.
DEBUG_DATA,
"GATT discoverDescriptors Characteristic[%d/%d]: %s on %s", charIter, charCount, charDecl->toString().c_str(), deviceString.c_str());
674 uint16_t cd_handle_iter = charDecl->value_handle + 1;
675 uint16_t cd_handle_end;
676 if( charIter+1 < charCount ) {
677 cd_handle_end = service->characteristicList.at(charIter+1)->value_handle;
679 cd_handle_end = service->endHandle;
684 while( !done && cd_handle_iter <= cd_handle_end ) {
695 for(
int e_iter=0; e_iter<e_count; e_iter++) {
701 std::shared_ptr<BTGattDesc> cd( std::make_shared<BTGattDesc>(charDecl, std::move(cd_uuid), cd_handle) );
702 if( cd_handle <= charDecl->value_handle || cd_handle > cd_handle_end ) {
703 ERR_PRINT(
"GATT discoverDescriptors CD handle %s not in range ]%s..%s]: descr%s within char%s on %s",
706 cd->
toString().c_str(), charDecl->toString().c_str(), deviceString.c_str());
712 WORDY_PRINT(
"GATT discoverDescriptors readDescriptorValue failed: req %s, descr%s within char%s on %s",
713 req.
toString().c_str(), cd->
toString().c_str(), charDecl->toString().c_str(), deviceString.c_str());
718 charDecl->clientCharConfigIndex = charDecl->descriptorList.size();
720 charDecl->descriptorList.push_back(cd);
724 if( cd_handle_iter < cd_handle_end ) {
732 ERR_PRINT(
"GATT discoverDescriptors unexpected reply %s; req %s within char%s from %s",
733 pdu->
toString().c_str(), req.
toString().c_str(), charDecl->toString().c_str(), deviceString.c_str());
740 return service->characteristicList.size() > 0;
747 WORDY_PRINT(
"GATT readDescriptorValue error on desc%s within char%s from %s",
757 WORDY_PRINT(
"GATT readCharacteristicValue error on char%s from %s", decl.
toString().c_str(), deviceString.c_str());
765 const std::lock_guard<std::recursive_mutex> lock(mtx_command);
774 if( 0 < expectedLength && expectedLength <= offset ) {
776 }
else if( 0 == expectedLength && 0 < offset ) {
780 std::unique_ptr<const AttPDUMsg> pdu =
nullptr;
822 WORDY_PRINT(
"GATT readValue unexpected error %s; req %s from %s", pdu->
toString().c_str(), req.
toString().c_str(), deviceString.c_str());
826 ERR_PRINT(
"GATT readValue unexpected reply %s; req %s from %s", pdu->
toString().c_str(), req.
toString().c_str(), deviceString.c_str());
843 WORDY_PRINT(
"GATT writeDescriptorValue error on desc%s within char%s from %s",
854 WORDY_PRINT(
"GATT writeCharacteristicValue error on char%s from %s", c.
toString().c_str(), deviceString.c_str());
875 const std::lock_guard<std::recursive_mutex> lock(mtx_command);
880 if( !withResponse ) {
900 WORDY_PRINT(
"GATT writeValue unexpected error %s; req %s from %s", pdu->
toString().c_str(), req.
toString().c_str(), deviceString.c_str());
902 ERR_PRINT(
"GATT writeValue unexpected reply %s; req %s from %s", pdu->
toString().c_str(), req.
toString().c_str(), deviceString.c_str());
913 const uint16_t ccc_value = enableNotification | ( enableIndication << 1 );
914 COND_PRINT(env.
DEBUG_DATA,
"GATTHandler::configNotificationIndication decl %s, enableNotification %d, enableIndication %d",
915 cccd.
toString().c_str(), enableNotification, enableIndication);
921 if( !enableNotification && !enableIndication ) {
923 WORDY_PRINT(
"GATTHandler::configNotificationIndication(disable) on %s caught exception: %s", deviceString.c_str(), bte.
what());
952 std::shared_ptr<GattGenericAccessSvc> res =
nullptr;
954 std::string deviceName =
"";
956 std::shared_ptr<GattPeriphalPreferredConnectionParameters> prefConnParam =
nullptr;
958 const std::lock_guard<std::recursive_mutex> lock(mtx_command);
960 for(
size_t i=0; i<genericAccessCharDeclList.
size(); i++) {
961 const BTGattChar & charDecl = *genericAccessCharDeclList.
at(i);
980 if( deviceName.size() > 0 ) {
981 res = std::make_shared<GattGenericAccessSvc>(deviceName, appearance, prefConnParam);
987 std::shared_ptr<GattGenericAccessSvc> res =
nullptr;
988 for(
size_t i=0; i<primServices.
size() &&
nullptr == res; i++) {
995 const std::lock_guard<std::recursive_mutex> lock(mtx_command);
998 for(
size_t i=0; isOK && i<services.
size(); i++) {
1002 for(
size_t j=0; isOK && j<genericAccessCharDeclList.
size(); j++) {
1003 const BTGattChar & charDecl = *genericAccessCharDeclList.
at(j);
1018 jau::INFO_PRINT(
"GATTHandler::pingGATT: No GENERIC_ACCESS Service with APPEARANCE Characteristic available -> disconnect");
1027 std::shared_ptr<GattDeviceInformationSvc> res =
nullptr;
1031 std::string modelNumber;
1032 std::string serialNumber;
1033 std::string firmwareRevision;
1034 std::string hardwareRevision;
1035 std::string softwareRevision;
1036 std::string manufacturer;
1037 POctets regulatoryCertDataList(128, 0);
1038 std::shared_ptr<GattPnP_ID> pnpID =
nullptr;
1041 const std::lock_guard<std::recursive_mutex> lock(mtx_command);
1043 for(
size_t i=0; i<characteristicDeclList.
size(); i++) {
1044 const BTGattChar & charDecl = *characteristicDeclList.
at(i);
1089 res = std::make_shared<GattDeviceInformationSvc>(systemID, modelNumber, serialNumber,
1090 firmwareRevision, hardwareRevision, softwareRevision,
1091 manufacturer, regulatoryCertDataList, pnpID);
1097 std::shared_ptr<GattDeviceInformationSvc> res =
nullptr;
1098 for(
size_t i=0; i<primServices.
size() &&
nullptr == res; i++) {