diff --git a/src/platform/Tizen/BLEManagerImpl.cpp b/src/platform/Tizen/BLEManagerImpl.cpp index 6e17b916c073d6..65c63247ecb8a4 100644 --- a/src/platform/Tizen/BLEManagerImpl.cpp +++ b/src/platform/Tizen/BLEManagerImpl.cpp @@ -59,6 +59,12 @@ const char * desc_uuid_short = "2902"; const char * chip_ble_service_uuid_short = "FFF6"; +/* Tizen Default Scan Timeout */ +static constexpr unsigned kNewConnectionScanTimeoutMs = 10000; + +/* Tizen Default Connect Timeout */ +constexpr System::Clock::Timeout kConnectTimeoutMs = System::Clock::Seconds16(10); + const int BtpServiceDataLenMax = 7; // OpCode(1) + Discriminator(2) + VendorId(2) + ProductId(2), 5.2.3.8.6. Advertising Data, CHIP Specification @@ -71,7 +77,16 @@ void BLEManagerImpl::GattConnectionStateChangedCb(int result, bool connected, co { ChipLogProgress(DeviceLayer, "Gatt Connection State Changed: %s result [%d]", connected ? "Connected" : "Disconnected", result); - sInstance.HandleConnectionEvent(connected, remoteAddress); + if (result != BT_ERROR_NONE) + { + ChipLogError(DeviceLayer, connected ? "Connection req failed" : "Disconnection req failed"); + if (connected) + sInstance.NotifyHandleConnectFailed(CHIP_ERROR_INTERNAL); + } + else + { + sInstance.HandleConnectionEvent(connected, remoteAddress); + } } gboolean BLEManagerImpl::_BleInitialize(void * userData) @@ -100,6 +115,7 @@ gboolean BLEManagerImpl::_BleInitialize(void * userData) sInstance.mFlags.Set(Flags::kTizenBLELayerInitialized); ChipLogProgress(DeviceLayer, "BLE Initialized"); + sInstance.mMainContext = g_main_context_get_thread_default(); return true; exit: @@ -203,6 +219,32 @@ void BLEManagerImpl::NotificationStateChangedCb(bool notify, bt_gatt_server_h se sInstance.NotifyBLESubscribed(notify ? true : false, conn); } +void BLEManagerImpl::WriteCompletedCb(int result, bt_gatt_h gattHandle, void * userData) +{ + BLEConnection * conn = nullptr; + conn = (BLEConnection *) userData; + + VerifyOrReturn(result == BT_ERROR_NONE, ChipLogError(DeviceLayer, "Failed to Send Write request")); + VerifyOrReturn(conn != nullptr, ChipLogError(DeviceLayer, "Connection object is invalid")); + VerifyOrReturn(conn->gattCharC1Handle == gattHandle, ChipLogError(DeviceLayer, "Gatt characteristic handle did not match")); + + ChipLogProgress(DeviceLayer, "Write Completed to CHIP peripheral [%s]", conn->peerAddr); + sInstance.NotifyHandleWriteComplete(conn); +} + +void BLEManagerImpl::CharacteristicNotificationCb(bt_gatt_h characteristic, char * value, int len, void * userData) +{ + BLEConnection * conn = nullptr; + conn = (BLEConnection *) userData; + + VerifyOrReturn(value != nullptr); + VerifyOrReturn(conn != nullptr, ChipLogError(DeviceLayer, "Connection object is invalid")); + VerifyOrReturn(conn->gattCharC2Handle == characteristic, ChipLogError(DeviceLayer, "Gatt characteristic handle did not match")); + + ChipLogProgress(DeviceLayer, "Notification Received from CHIP peripheral [%s]", conn->peerAddr); + sInstance.HandleRXCharChanged(conn, (const uint8_t *) value, len); +} + void BLEManagerImpl::IndicationConfirmationCb(int result, const char * remoteAddress, bt_gatt_server_h server, bt_gatt_h characteristic, bool completed, void * userData) { @@ -289,6 +331,15 @@ void BLEManagerImpl::NotifyBLEWriteReceived(System::PacketBufferHandle & buf, BL PlatformMgr().PostEventOrDie(&event); } +void BLEManagerImpl::NotifyBLENotificationReceived(System::PacketBufferHandle & buf, BLE_CONNECTION_OBJECT conId) +{ + ChipDeviceEvent event; + event.Type = DeviceEventType::kPlatformTizenBLEIndicationReceived; + event.Platform.BLEIndicationReceived.mConnection = conId; + event.Platform.BLEIndicationReceived.mData = std::move(buf).UnsafeRelease(); + PlatformMgr().PostEventOrDie(&event); +} + void BLEManagerImpl::NotifyBLESubscribed(bool indicationsEnabled, BLE_CONNECTION_OBJECT conId) { ChipDeviceEvent event; @@ -321,6 +372,136 @@ void BLEManagerImpl::NotifyBLEDisconnection(BLE_CONNECTION_OBJECT conId, CHIP_ER PlatformMgr().PostEventOrDie(&event); } +void BLEManagerImpl::NotifyHandleConnectFailed(CHIP_ERROR error) +{ + if (sInstance.mIsCentral) + { + ChipLogProgress(DeviceLayer, "Connection Failed: Post Platform event"); + ChipDeviceEvent event; + event.Type = DeviceEventType::kPlatformTizenBLECentralConnectFailed; + event.Platform.BLECentralConnectFailed.mError = error; + PlatformMgr().PostEventOrDie(&event); + } +} + +void BLEManagerImpl::NotifyHandleNewConnection(BLE_CONNECTION_OBJECT conId) +{ + if (sInstance.mIsCentral) + { + ChipDeviceEvent event; + event.Type = DeviceEventType::kPlatformTizenBLECentralConnected; + event.Platform.BLECentralConnected.mConnection = conId; + PlatformMgr().PostEventOrDie(&event); + } +} + +void BLEManagerImpl::NotifyHandleWriteComplete(BLE_CONNECTION_OBJECT conId) +{ + ChipDeviceEvent event; + event.Type = DeviceEventType::kPlatformTizenBLEWriteComplete; + event.Platform.BLEWriteComplete.mConnection = conId; + PlatformMgr().PostEventOrDie(&event); +} + +void BLEManagerImpl::NotifySubscribeOpComplete(BLE_CONNECTION_OBJECT conId, bool isSubscribed) +{ + ChipDeviceEvent event; + event.Type = DeviceEventType::kPlatformTizenBLESubscribeOpComplete; + event.Platform.BLESubscribeOpComplete.mConnection = conId; + event.Platform.BLESubscribeOpComplete.mIsSubscribed = isSubscribed; + PlatformMgr().PostEventOrDie(&event); +} + +void BLEManagerImpl::HandleConnectionTimeout(chip::System::Layer * layer, void * data) +{ + sInstance.NotifyHandleConnectFailed(CHIP_ERROR_TIMEOUT); +} + +gboolean BLEManagerImpl::ConnectChipThing(gpointer userData) +{ + int ret = BT_ERROR_NONE; + + char * address = (char *) userData; + ChipLogProgress(DeviceLayer, "ConnectRequest: Addr [%s]", address); + + ret = bt_gatt_client_create(address, &sInstance.mGattClient); + VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "Failed to create GATT client. ret [%d]", ret)); + + ret = bt_gatt_connect(address, false); + VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "Failed to issue GATT connect request. ret [%d]", ret)); + + ChipLogProgress(DeviceLayer, "GATT Connect Issued"); +exit: + if (ret != BT_ERROR_NONE) + sInstance.NotifyHandleConnectFailed(CHIP_ERROR_INTERNAL); + + g_free(address); + return G_SOURCE_REMOVE; +} + +void BLEManagerImpl::ConnectHandler(const char * address) +{ + GSource * idleSource; + + idleSource = g_idle_source_new(); + g_source_set_callback(idleSource, ConnectChipThing, g_strdup(address), nullptr); + g_source_set_priority(idleSource, G_PRIORITY_HIGH_IDLE); + g_source_attach(idleSource, sInstance.mMainContext); + g_source_unref(idleSource); +} + +void BLEManagerImpl::OnChipDeviceScanned(void * device, const chip::Ble::ChipBLEDeviceIdentificationInfo & info) +{ + bt_adapter_le_device_scan_result_info_s * deviceInfo = (bt_adapter_le_device_scan_result_info_s *) device; + VerifyOrReturn(deviceInfo != nullptr, ChipLogError(DeviceLayer, "Invalid Device Info")); + + ChipLogProgress(DeviceLayer, "New device scanned: %s", deviceInfo->remote_address); + + if (mBLEScanConfig.mBleScanState == BleScanState::kScanForDiscriminator) + { + if (info.GetDeviceDiscriminator() != mBLEScanConfig.mDiscriminator) + { + return; + } + ChipLogProgress(DeviceLayer, "Device discriminator match. Attempting to connect."); + } + else if (mBLEScanConfig.mBleScanState == BleScanState::kScanForAddress) + { + if (strcmp(deviceInfo->remote_address, mBLEScanConfig.mAddress.c_str()) != 0) + { + return; + } + ChipLogProgress(DeviceLayer, "Device address match. Attempting to connect."); + } + else + { + ChipLogError(DeviceLayer, "Unknown discovery type. Ignoring scanned device."); + return; + } + + /* Set CHIP Connecting state */ + mBLEScanConfig.mBleScanState = BleScanState::kConnecting; + chip::DeviceLayer::SystemLayer().StartTimer(kConnectTimeoutMs, HandleConnectionTimeout, nullptr); + mDeviceScanner->StopChipScan(); + + /* Initiate Connect */ + ConnectHandler(deviceInfo->remote_address); +} + +void BLEManagerImpl::OnChipScanComplete(void) +{ + if (mBLEScanConfig.mBleScanState != BleScanState::kScanForDiscriminator && + mBLEScanConfig.mBleScanState != BleScanState::kScanForAddress) + { + ChipLogProgress(DeviceLayer, "Scan complete notification without an active scan."); + return; + } + + ChipLogError(DeviceLayer, "Scan Completed with Timeout: Notify Upstream."); + BleConnectionDelegate::OnConnectionError(mBLEScanConfig.mAppState, CHIP_ERROR_TIMEOUT); + mBLEScanConfig.mBleScanState = BleScanState::kNotScanning; +} + int BLEManagerImpl::RegisterGATTServer() { int ret = BT_ERROR_NONE; @@ -497,6 +678,67 @@ void BLEManagerImpl::InitConnectionData(void) } } +static bool __GattClientForeachCharCb(int total, int index, bt_gatt_h charHandle, void * data) +{ + int type; + char * uuid = NULL; + BLEConnection * conn = (BLEConnection *) data; + + if (__GetAttInfo(charHandle, &uuid, &type) == BT_ERROR_NONE) + { + if (strcasecmp(uuid, chip_ble_char_c1_tx_uuid) == 0) + { + ChipLogProgress(DeviceLayer, "CHIP Char C1 TX Found [%s]", uuid); + conn->gattCharC1Handle = charHandle; + } + else if (strcasecmp(uuid, chip_ble_char_c2_rx_uuid) == 0) + { + ChipLogProgress(DeviceLayer, "CHIP Char C2 RX Found [%s]", uuid); + conn->gattCharC2Handle = charHandle; + } + g_free(uuid); + } + /* Try next Char UUID */ + return true; +} + +static bool __GattClientForeachServiceCb(int total, int index, bt_gatt_h svcHandle, void * data) +{ + int type; + char * uuid = NULL; + BLEConnection * conn = (BLEConnection *) data; + ChipLogProgress(DeviceLayer, "__GattClientForeachServiceCb"); + + if (__GetAttInfo(svcHandle, &uuid, &type) == BT_ERROR_NONE) + { + if (strcasecmp(uuid, chip_ble_service_uuid) == 0) + { + ChipLogProgress(DeviceLayer, "CHIP Service UUID Found [%s]", uuid); + + if (bt_gatt_service_foreach_characteristics(svcHandle, __GattClientForeachCharCb, conn) == BT_ERROR_NONE) + conn->isChipDevice = true; + + /* Got CHIP Device, no need to process further service */ + g_free(uuid); + return false; + } + g_free(uuid); + } + /* Try next Service UUID */ + return true; +} + +bool BLEManagerImpl::IsDeviceChipPeripheral(void * device) +{ + BLEConnection * bleConn; + bleConn = (BLEConnection *) device; + + if (bt_gatt_client_foreach_services(sInstance.mGattClient, __GattClientForeachServiceCb, bleConn) != BT_ERROR_NONE) + ChipLogError(DeviceLayer, "Error Browsing GATT services"); + + return (bleConn->isChipDevice ? true : false); +} + void BLEManagerImpl::AddConnectionData(const char * remoteAddr) { BLEConnection * conn; @@ -507,25 +749,56 @@ void BLEManagerImpl::AddConnectionData(const char * remoteAddr) ChipLogProgress(DeviceLayer, "Not Found in Map"); conn = (BLEConnection *) g_malloc0(sizeof(BLEConnection)); conn->peerAddr = g_strdup(remoteAddr); - g_hash_table_insert(mConnectionMap, (gpointer) conn->peerAddr, conn); - ChipLogProgress(DeviceLayer, "New Connection Added for [%s]", remoteAddr); + + if (sInstance.mIsCentral) + { + /* Local Device is BLE Central Role */ + if (IsDeviceChipPeripheral(conn)) + { + g_hash_table_insert(mConnectionMap, (gpointer) conn->peerAddr, conn); + ChipLogProgress(DeviceLayer, "New Connection Added for [%s]", remoteAddr); + NotifyHandleNewConnection(conn); + } + else + { + g_free(conn->peerAddr); + g_free(conn); + } + } + else + { + /* Local Device is BLE Peripheral Role, assume remote is CHIP Central */ + conn->isChipDevice = true; + + /* Save own gatt handles */ + conn->gattCharC1Handle = mGattCharC1Handle; + conn->gattCharC2Handle = mGattCharC2Handle; + + g_hash_table_insert(mConnectionMap, (gpointer) conn->peerAddr, conn); + ChipLogProgress(DeviceLayer, "New Connection Added for [%s]", remoteAddr); + } } } void BLEManagerImpl::RemoveConnectionData(const char * remoteAddr) { BLEConnection * conn = nullptr; + ChipLogProgress(DeviceLayer, "Connection Remove Request for [%s]", remoteAddr); + + VerifyOrReturn(mConnectionMap != nullptr, ChipLogError(DeviceLayer, "connection map does not exist")); conn = (BLEConnection *) g_hash_table_lookup(mConnectionMap, remoteAddr); - if (!conn) - return; + VerifyOrReturn(conn != nullptr, ChipLogError(DeviceLayer, "connection does not exist for [%s]", remoteAddr)); - g_hash_table_remove(mConnectionMap, remoteAddr); + g_hash_table_remove(mConnectionMap, conn->peerAddr); g_free(conn->peerAddr); g_free(conn); - ChipLogProgress(DeviceLayer, "Connection Removed for [%s]", remoteAddr); + if (!g_hash_table_size(mConnectionMap)) + mConnectionMap = nullptr; + + ChipLogProgress(DeviceLayer, "Connection Removed"); } void BLEManagerImpl::HandleC1CharWriteEvent(BLE_CONNECTION_OBJECT conId, const uint8_t * value, size_t len) @@ -546,6 +819,24 @@ void BLEManagerImpl::HandleC1CharWriteEvent(BLE_CONNECTION_OBJECT conId, const u } } +void BLEManagerImpl::HandleRXCharChanged(BLE_CONNECTION_OBJECT conId, const uint8_t * value, size_t len) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + System::PacketBufferHandle buf; + + ChipLogProgress(DeviceLayer, "Notification received on CHIPoBLE Client RX characteristic (data len %u)", len); + // Copy the data to a packet buffer. + buf = System::PacketBufferHandle::NewWithData(value, len); + VerifyOrExit(!buf.IsNull(), err = CHIP_ERROR_NO_MEMORY); + NotifyBLENotificationReceived(buf, conId); + return; +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(DeviceLayer, "HandleRXCharChanged() failed: %s", ErrorStr(err)); + } +} + void BLEManagerImpl::HandleConnectionEvent(bool connected, const char * remoteAddress) { if (connected) @@ -555,11 +846,7 @@ void BLEManagerImpl::HandleConnectionEvent(bool connected, const char * remoteAd } else { - BLEConnection * conn = nullptr; ChipLogProgress(DeviceLayer, "Device DisConnected [%s]", remoteAddress); - conn = (BLEConnection *) g_hash_table_lookup(mConnectionMap, remoteAddress); - /* Tizen Platform does not return GATT disconnection reason, hence fallback to default */ - NotifyBLEDisconnection(conn, BLE_ERROR_REMOTE_DEVICE_DISCONNECTED); RemoveConnectionData(remoteAddress); } } @@ -735,11 +1022,72 @@ CHIP_ERROR BLEManagerImpl::ConfigureBle(uint32_t aAdapterId, bool aIsCentral) return CHIP_NO_ERROR; } +void BLEManagerImpl::CleanScanConfig(void) +{ + if (mBLEScanConfig.mBleScanState == BleScanState::kConnecting) + chip::DeviceLayer::SystemLayer().CancelTimer(HandleConnectionTimeout, nullptr); + + mBLEScanConfig.mBleScanState = BleScanState::kNotScanning; +} + void BLEManagerImpl::HandlePlatformSpecificBLEEvent(const ChipDeviceEvent * apEvent) { ChipLogDetail(DeviceLayer, "HandlePlatformSpecificBLEEvent %d", apEvent->Type); - // TODO: Need to implement Tizen Platform Specific events: CHIPDevicePlatformEvent + switch (apEvent->Type) + { + case DeviceEventType::kPlatformTizenBLECentralConnected: + if (mBLEScanConfig.mBleScanState == BleScanState::kConnecting) + { + BleConnectionDelegate::OnConnectionComplete(mBLEScanConfig.mAppState, + apEvent->Platform.BLECentralConnected.mConnection); + CleanScanConfig(); + } + break; + case DeviceEventType::kPlatformTizenBLECentralConnectFailed: + if (mBLEScanConfig.mBleScanState == BleScanState::kConnecting) + { + BleConnectionDelegate::OnConnectionError(mBLEScanConfig.mAppState, apEvent->Platform.BLECentralConnectFailed.mError); + CleanScanConfig(); + } + break; + case DeviceEventType::kPlatformTizenBLEWriteComplete: { + ChipBleUUID service_uuid; + ChipBleUUID char_write_uuid; + + StringToUUID(chip_ble_service_uuid, service_uuid); + StringToUUID(chip_ble_char_c1_tx_uuid, char_write_uuid); + + HandleWriteConfirmation(apEvent->Platform.BLEWriteComplete.mConnection, &service_uuid, &char_write_uuid); + break; + } + case DeviceEventType::kPlatformTizenBLESubscribeOpComplete: { + ChipBleUUID service_uuid; + ChipBleUUID char_notif_uuid; + + StringToUUID(chip_ble_service_uuid, service_uuid); + StringToUUID(chip_ble_char_c2_rx_uuid, char_notif_uuid); + + if (apEvent->Platform.BLESubscribeOpComplete.mIsSubscribed) + HandleSubscribeComplete(apEvent->Platform.BLESubscribeOpComplete.mConnection, &service_uuid, &char_notif_uuid); + else + HandleUnsubscribeComplete(apEvent->Platform.BLESubscribeOpComplete.mConnection, &service_uuid, &char_notif_uuid); + break; + } + case DeviceEventType::kPlatformTizenBLEIndicationReceived: { + ChipBleUUID service_uuid; + ChipBleUUID char_notif_uuid; + + StringToUUID(chip_ble_service_uuid, service_uuid); + StringToUUID(chip_ble_char_c2_rx_uuid, char_notif_uuid); + + HandleIndicationReceived(apEvent->Platform.BLEIndicationReceived.mConnection, &service_uuid, &char_notif_uuid, + PacketBufferHandle::Adopt(apEvent->Platform.BLEIndicationReceived.mData)); + break; + } + default: + break; + } } void BLEManagerImpl::_OnPlatformEvent(const ChipDeviceEvent * event) @@ -804,16 +1152,82 @@ uint16_t BLEManagerImpl::GetMTU(BLE_CONNECTION_OBJECT conId) const bool BLEManagerImpl::SubscribeCharacteristic(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId) { + ChipBleUUID service_uuid; + ChipBleUUID char_notif_uuid; + int ret = BT_ERROR_NONE; + BLEConnection * conn = (BLEConnection *) conId; + + ChipLogProgress(DeviceLayer, "SubscribeCharacteristic"); + + StringToUUID(chip_ble_service_uuid, service_uuid); + StringToUUID(chip_ble_char_c2_rx_uuid, char_notif_uuid); + + VerifyOrExit(conn != nullptr, ChipLogError(DeviceLayer, "Invalid Connection")); + VerifyOrExit(Ble::UUIDsMatch(svcId, &service_uuid), + ChipLogError(DeviceLayer, "SubscribeCharacteristic() called with invalid service ID")); + VerifyOrExit(Ble::UUIDsMatch(charId, &char_notif_uuid), + ChipLogError(DeviceLayer, "SubscribeCharacteristic() called with invalid characteristic ID")); + VerifyOrExit(conn->gattCharC2Handle != nullptr, ChipLogError(DeviceLayer, "Char C2 is null")); + + ChipLogProgress(DeviceLayer, "Sending Notification Enable Request to CHIP Peripheral(con %s)", conn->peerAddr); + + ret = bt_gatt_client_set_characteristic_value_changed_cb(conn->gattCharC2Handle, CharacteristicNotificationCb, conn); + VerifyOrExit(ret == BT_ERROR_NONE, + ChipLogError(DeviceLayer, "bt_gatt_client_set_characteristic_value_changed_cb() failed. ret: %d", ret)); + sInstance.NotifySubscribeOpComplete(conn, true); + return true; +exit: return false; } bool BLEManagerImpl::UnsubscribeCharacteristic(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId) { + ChipBleUUID service_uuid; + ChipBleUUID char_notif_uuid; + int ret = BT_ERROR_NONE; + BLEConnection * conn = (BLEConnection *) conId; + + ChipLogProgress(DeviceLayer, "UnSubscribeCharacteristic"); + + StringToUUID(chip_ble_service_uuid, service_uuid); + StringToUUID(chip_ble_char_c2_rx_uuid, char_notif_uuid); + + VerifyOrExit(conn != nullptr, ChipLogError(DeviceLayer, "Invalid Connection")); + VerifyOrExit(Ble::UUIDsMatch(svcId, &service_uuid), + ChipLogError(DeviceLayer, "UnSubscribeCharacteristic() called with invalid service ID")); + VerifyOrExit(Ble::UUIDsMatch(charId, &char_notif_uuid), + ChipLogError(DeviceLayer, "UnSubscribeCharacteristic() called with invalid characteristic ID")); + VerifyOrExit(conn->gattCharC2Handle != nullptr, ChipLogError(DeviceLayer, "Char C2 is null")); + + ChipLogProgress(DeviceLayer, "Disable Notification Request to CHIP Peripheral(con %s)", conn->peerAddr); + + ret = bt_gatt_client_unset_characteristic_value_changed_cb(conn->gattCharC2Handle); + VerifyOrExit(ret == BT_ERROR_NONE, + ChipLogError(DeviceLayer, "bt_gatt_client_unset_characteristic_value_changed_cb() failed. ret: %d", ret)); + sInstance.NotifySubscribeOpComplete(conn, false); + return true; +exit: return false; } bool BLEManagerImpl::CloseConnection(BLE_CONNECTION_OBJECT conId) { + int ret = BT_ERROR_NONE; + BLEConnection * conn = nullptr; + + ChipLogProgress(DeviceLayer, "Close BLE Connection"); + + conn = (BLEConnection *) g_hash_table_lookup(sInstance.mConnectionMap, ((BLEConnection *) conId)->peerAddr); + VerifyOrExit(conn != nullptr, ChipLogError(DeviceLayer, "Failed to find connection info")); + + ChipLogProgress(DeviceLayer, "Send GATT disconnect to [%s]", conn->peerAddr); + ret = bt_gatt_disconnect(conn->peerAddr); + VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "bt_gatt_disconnect() failed. ret: %d", ret)); + + RemoveConnectionData(conn->peerAddr); + return true; + +exit: return false; } @@ -846,6 +1260,33 @@ bool BLEManagerImpl::SendIndication(BLE_CONNECTION_OBJECT conId, const ChipBleUU bool BLEManagerImpl::SendWriteRequest(BLE_CONNECTION_OBJECT conId, const Ble::ChipBleUUID * svcId, const Ble::ChipBleUUID * charId, chip::System::PacketBufferHandle pBuf) { + ChipBleUUID service_uuid; + ChipBleUUID char_write_uuid; + int ret = BT_ERROR_NONE; + BLEConnection * conn = (BLEConnection *) conId; + + ChipLogProgress(DeviceLayer, "SendWriteRequest"); + + StringToUUID(chip_ble_service_uuid, service_uuid); + StringToUUID(chip_ble_char_c1_tx_uuid, char_write_uuid); + + VerifyOrExit(conn != nullptr, ChipLogError(DeviceLayer, "Invalid Connection")); + VerifyOrExit(Ble::UUIDsMatch(svcId, &service_uuid), + ChipLogError(DeviceLayer, "SendWriteRequest() called with invalid service ID")); + VerifyOrExit(Ble::UUIDsMatch(charId, &char_write_uuid), + ChipLogError(DeviceLayer, "SendWriteRequest() called with invalid characteristic ID")); + VerifyOrExit(conn->gattCharC1Handle != nullptr, ChipLogError(DeviceLayer, "Char C1 is null")); + + ret = bt_gatt_set_value(conn->gattCharC1Handle, (const char *) pBuf->Start(), pBuf->DataLength()); + VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "bt_gatt_set_value() failed. ret: %d", ret)); + + ChipLogProgress(DeviceLayer, "Sending Write Request for CHIPoBLE TX characteristic (con %s, len %u)", conn->peerAddr, + pBuf->DataLength()); + + ret = bt_gatt_client_write_value(conn->gattCharC1Handle, WriteCompletedCb, conn); + VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "bt_gatt_client_write_value() failed. ret: %d", ret)); + return true; +exit: return false; } @@ -863,7 +1304,78 @@ bool BLEManagerImpl::SendReadResponse(BLE_CONNECTION_OBJECT conId, BLE_READ_REQU void BLEManagerImpl::NotifyChipConnectionClosed(BLE_CONNECTION_OBJECT conId) {} -void BLEManagerImpl::NewConnection(BleLayer * bleLayer, void * appState, const uint16_t connDiscriminator) {} +void BLEManagerImpl::NewConnection(BleLayer * bleLayer, void * appState, const uint16_t connDiscriminator) +{ + mBLEScanConfig.mDiscriminator = connDiscriminator; + mBLEScanConfig.mAppState = appState; + ChipLogProgress(DeviceLayer, "NewConnection: discriminator value [%u]", connDiscriminator); + + // Initiate Scan. + PlatformMgr().ScheduleWork(InitiateScan, static_cast(BleScanState::kScanForDiscriminator)); +} + +void BLEManagerImpl::InitiateScan(BleScanState scanType) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + ScanFilterData data = { + 0x0, + }; + + ChipLogProgress(DeviceLayer, "Initiate Scan"); + + /* Check Scanning state */ + if (scanType == BleScanState::kNotScanning) + { + err = CHIP_ERROR_INCORRECT_STATE; + ChipLogError(DeviceLayer, "Invalid scan type requested"); + goto exit; + } + /* Check Tizen BLE layer is initialized or not */ + if (!mFlags.Has(Flags::kTizenBLELayerInitialized)) + { + err = CHIP_ERROR_INCORRECT_STATE; + ChipLogError(DeviceLayer, "Tizen BLE Layer is not yet initialized"); + goto exit; + } + /* Setup ScanFilter */ + memset(&data.service_data, 0x00, sizeof(data.service_data)); + data.service_data_len = 0; + strcpy(data.service_uuid, chip_ble_service_uuid_short); + + /* Acquire Chip Device Scanner */ + if (!mDeviceScanner) + mDeviceScanner = Internal::ChipDeviceScanner::Create(this); + + if (!mDeviceScanner) + { + err = CHIP_ERROR_INTERNAL; + ChipLogError(DeviceLayer, "Failed to create a BLE device scanner"); + goto exit; + } + + /* Send StartChipScan Request to Scanner Class */ + err = mDeviceScanner->StartChipScan(kNewConnectionScanTimeoutMs, ScanFilterType::kServiceData, data); + + if (err != CHIP_NO_ERROR) + { + ChipLogError(DeviceLayer, "Failed to start a BLE Scan: %s", chip::ErrorStr(err)); + goto exit; + } + + ChipLogError(DeviceLayer, "BLE Scan Initiation Successful"); + mBLEScanConfig.mBleScanState = scanType; + return; + +exit: + ChipLogError(DeviceLayer, "Scan Initiation Failed!"); + mBLEScanConfig.mBleScanState = BleScanState::kNotScanning; + BleConnectionDelegate::OnConnectionError(mBLEScanConfig.mAppState, err); +} + +void BLEManagerImpl::InitiateScan(intptr_t arg) +{ + sInstance.InitiateScan(static_cast(arg)); +} CHIP_ERROR BLEManagerImpl::CancelConnection() { diff --git a/src/platform/Tizen/BLEManagerImpl.h b/src/platform/Tizen/BLEManagerImpl.h index 16e5c1512e506f..9f84d190b9e27a 100644 --- a/src/platform/Tizen/BLEManagerImpl.h +++ b/src/platform/Tizen/BLEManagerImpl.h @@ -29,12 +29,43 @@ #include #include +#include "ChipDeviceScanner.h" + namespace chip { namespace DeviceLayer { namespace Internal { using namespace chip::Ble; +/** + * enum Class for BLE Scanning state. CHIP supports Scanning by Discriminator or Address + */ +enum class BleScanState : uint8_t +{ + kNotScanning, + kScanForDiscriminator, + kScanForAddress, + kConnecting, +}; + +/** + * Structure for BLE Scanning Configuration + */ +struct BLEScanConfig +{ + // If an active scan for connection is being performed + BleScanState mBleScanState = BleScanState::kNotScanning; + + // If scanning by discriminator, what are we scanning for + uint16_t mDiscriminator = 0; + + // If scanning by address, what address are we searching for + std::string mAddress; + + // Optional argument to be passed to callback functions provided by the BLE scan/connect requestor + void * mAppState = nullptr; +}; + /** * Concrete implementation of the BLEManagerImpl singleton object for the Tizen platforms. */ @@ -42,7 +73,8 @@ class BLEManagerImpl final : public BLEManager, private Ble::BleLayer, private Ble::BlePlatformDelegate, private Ble::BleApplicationDelegate, - private Ble::BleConnectionDelegate + private Ble::BleConnectionDelegate, + private ChipDeviceScannerDelegate { // Allow the BLEManager interface class to delegate method calls to // the implementation methods provided by this class. @@ -95,6 +127,10 @@ class BLEManagerImpl final : public BLEManager, void NewConnection(BleLayer * bleLayer, void * appState, uint16_t connDiscriminator) override; CHIP_ERROR CancelConnection() override; + // ===== Members that implement virtual methods on ChipDeviceScannerDelegate + void OnChipDeviceScanned(void * device, const chip::Ble::ChipBLEDeviceIdentificationInfo & info) override; + void OnChipScanComplete() override; + // ===== Members for internal use by the following friends. friend BLEManager & BLEMgr(void); @@ -120,6 +156,10 @@ class BLEManagerImpl final : public BLEManager, static gboolean _BleInitialize(void * userData); void DriveBLEState(); static void DriveBLEState(intptr_t arg); + + void InitiateScan(BleScanState scanType); + static void InitiateScan(intptr_t arg); + static void AdvertisingStateChangedCb(int result, bt_advertiser_h advertiser, bt_adapter_le_advertising_state_e advState, void * userData); static void NotificationStateChangedCb(bool notify, bt_gatt_server_h server, bt_gatt_h gattHandle, void * userData); @@ -130,6 +170,8 @@ class BLEManagerImpl final : public BLEManager, static void IndicationConfirmationCb(bt_gatt_h characteristic, bt_gatt_server_notification_sent_cb callback, const char * device_address, void * userData); static void GattConnectionStateChangedCb(int result, bool connected, const char * remoteAddress, void * userData); + static void WriteCompletedCb(int result, bt_gatt_h gattHandle, void * userData); + static void CharacteristicNotificationCb(bt_gatt_h characteristic, char * value, int len, void * userData); // ==== Connection. void InitConnectionData(void); @@ -137,7 +179,10 @@ class BLEManagerImpl final : public BLEManager, void RemoveConnectionData(const char * remoteAddr); void HandleC1CharWriteEvent(BLE_CONNECTION_OBJECT conId, const uint8_t * value, size_t len); + void HandleRXCharChanged(BLE_CONNECTION_OBJECT conId, const uint8_t * value, size_t len); void HandleConnectionEvent(bool connected, const char * remoteAddress); + static void HandleConnectionTimeout(chip::System::Layer * layer, void * data); + static bool IsDeviceChipPeripheral(void * device); // ==== BLE Adv & GATT Server. void NotifyBLEPeripheralGATTServerRegisterComplete(bool aIsSuccess, void * apAppstate); @@ -147,12 +192,22 @@ class BLEManagerImpl final : public BLEManager, void NotifyBLESubscribed(bool indicationsEnabled, BLE_CONNECTION_OBJECT conId); void NotifyBLEIndicationConfirmation(BLE_CONNECTION_OBJECT conId); void NotifyBLEWriteReceived(System::PacketBufferHandle & buf, BLE_CONNECTION_OBJECT conId); + + // ==== Connection. + void ConnectHandler(const char * address); + static gboolean ConnectChipThing(gpointer userData); void NotifyBLEConnectionEstablished(BLE_CONNECTION_OBJECT conId, CHIP_ERROR error); void NotifyBLEDisconnection(BLE_CONNECTION_OBJECT conId, CHIP_ERROR error); + void NotifyHandleNewConnection(BLE_CONNECTION_OBJECT conId); + void NotifyHandleConnectFailed(CHIP_ERROR error); + void NotifyHandleWriteComplete(BLE_CONNECTION_OBJECT conId); + void NotifySubscribeOpComplete(BLE_CONNECTION_OBJECT conId, bool isSubscribed); + void NotifyBLENotificationReceived(System::PacketBufferHandle & buf, BLE_CONNECTION_OBJECT conId); int RegisterGATTServer(void); int StartAdvertising(void); int StopAdvertising(void); + void CleanScanConfig(void); CHIPoBLEServiceMode mServiceMode; BitFlags mFlags; @@ -164,6 +219,11 @@ class BLEManagerImpl final : public BLEManager, bool mAdvReqInProgress = false; /* Connection Hash Table Map */ GHashTable * mConnectionMap = nullptr; + + BLEScanConfig mBLEScanConfig; + std::unique_ptr mDeviceScanner; + GMainContext * mMainContext = nullptr; + bt_gatt_client_h mGattClient = nullptr; }; /** diff --git a/src/platform/Tizen/BUILD.gn b/src/platform/Tizen/BUILD.gn index f066f8892819b5..64002e10607876 100644 --- a/src/platform/Tizen/BUILD.gn +++ b/src/platform/Tizen/BUILD.gn @@ -35,6 +35,8 @@ static_library("Tizen") { "CHIPDevicePlatformConfig.h", "CHIPDevicePlatformEvent.h", "CHIPPlatformConfig.h", + "ChipDeviceScanner.cpp", + "ChipDeviceScanner.h", "ConfigurationManagerImpl.cpp", "ConfigurationManagerImpl.h", "ConnectivityManagerImpl.cpp", diff --git a/src/platform/Tizen/CHIPDevicePlatformEvent.h b/src/platform/Tizen/CHIPDevicePlatformEvent.h index c08ca4ed9e77b4..ffca49599bfe3d 100644 --- a/src/platform/Tizen/CHIPDevicePlatformEvent.h +++ b/src/platform/Tizen/CHIPDevicePlatformEvent.h @@ -47,7 +47,12 @@ enum InternalPlatformSpecificEventTypes kPlatformTizenBLEPeripheralGATTServerRegisterComplete, kPlatformTizenBLEPeripheralAdvConfiguredComplete, kPlatformTizenBLEPeripheralAdvStartComplete, - kPlatformTizenBLEPeripheralAdvStopComplete + kPlatformTizenBLEPeripheralAdvStopComplete, + kPlatformTizenBLECentralConnected, + kPlatformTizenBLECentralConnectFailed, + kPlatformTizenBLEWriteComplete, + kPlatformTizenBLESubscribeOpComplete, + kPlatformTizenBLEIndicationReceived }; } // namespace DeviceEventType @@ -79,6 +84,28 @@ struct ChipDevicePlatformEvent bool mIsSuccess; void * mpAppstate; } BLEPeripheralAdvStopComplete; + struct + { + BLE_CONNECTION_OBJECT mConnection; + } BLECentralConnected; + struct + { + CHIP_ERROR mError; + } BLECentralConnectFailed; + struct + { + BLE_CONNECTION_OBJECT mConnection; + } BLEWriteComplete; + struct + { + BLE_CONNECTION_OBJECT mConnection; + bool mIsSubscribed; + } BLESubscribeOpComplete; + struct + { + BLE_CONNECTION_OBJECT mConnection; + chip::System::PacketBuffer * mData; + } BLEIndicationReceived; }; }; diff --git a/src/platform/Tizen/ChipDeviceScanner.cpp b/src/platform/Tizen/ChipDeviceScanner.cpp new file mode 100644 index 00000000000000..4a278438390640 --- /dev/null +++ b/src/platform/Tizen/ChipDeviceScanner.cpp @@ -0,0 +1,321 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * Provides an implementation of the Matter Device Scanner + * for the Tizen platforms + */ + +#include "ChipDeviceScanner.h" +#include +#include + +#if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE + +#include "MainLoop.h" +#include +#include + +namespace chip { +namespace DeviceLayer { +namespace Internal { + +// CHIPoBLE UUID strings +const char * chip_service_uuid = "0000FFF6-0000-1000-8000-00805F9B34FB"; +const char * chip_service_uuid_short = "FFF6"; + +// Default CHIP Scan Timeout in Millisecond +static unsigned int kScanTimeout = 10000; + +ChipDeviceScanner::ChipDeviceScanner(ChipDeviceScannerDelegate * delegate) : mDelegate(delegate) {} + +ChipDeviceScanner::~ChipDeviceScanner() +{ + // In case scan is ongoing + StopChipScan(); + mDelegate = nullptr; +} + +std::unique_ptr ChipDeviceScanner::Create(ChipDeviceScannerDelegate * delegate) +{ + return std::make_unique(delegate); +} + +static void __CleanupServiceData(bt_adapter_le_service_data_s * dataList, int count) +{ + int i; + if (!dataList || count == 0) + return; + + for (i = 0; i < count; i++) + { + g_free(dataList[i].service_uuid); + g_free(dataList[i].service_data); + } + g_free(dataList); +} + +static void __PrintLEScanData(bt_adapter_le_service_data_s * dataList, int idx) +{ + int i = 0; + if (!dataList) + return; + + // Print Service UUID in the Service Data + ChipLogProgress(DeviceLayer, "======Service UUID========\n"); + ChipLogProgress(DeviceLayer, " UUID[%s]\n", dataList[idx].service_uuid); + + ChipLogProgress(DeviceLayer, "Service Data Length::[%d]\n", dataList[idx].service_data_len); + + // Print Service Data + if (dataList[idx].service_data_len > 0) + ChipLogProgress(DeviceLayer, "=====Service Data====\n"); + + for (i = 0; i < dataList[idx].service_data_len; i++) + { + ChipLogProgress(DeviceLayer, "Service Data[%d] = [0x%x]\n", i, dataList[idx].service_data[i]); + } +} + +static bool __IsChipThingDevice(bt_adapter_le_device_scan_result_info_s * info, + chip::Ble::ChipBLEDeviceIdentificationInfo & aDeviceInfo) +{ + int count = 0; + bt_adapter_le_service_data_s * dataList = nullptr; + bool isChipDevice = false; + + if (!info) + return false; + + ChipLogError(DeviceLayer, "Is [%s] ChipThingDevice ?: Check now", info->remote_address); + + if (bt_adapter_le_get_scan_result_service_data_list(info, BT_ADAPTER_LE_PACKET_ADVERTISING, &dataList, &count) == BT_ERROR_NONE) + { + int i; + for (i = 0; i < count; i++) + { + if (g_strcmp0(dataList[i].service_uuid, chip_service_uuid) == 0 || + g_strcmp0(dataList[i].service_uuid, chip_service_uuid_short) == 0) + { + ChipLogProgress(DeviceLayer, "CHIP Thing Device Found! [Service Data UUID] = %s", dataList[i].service_uuid); + // Print full Service Data + __PrintLEScanData(dataList, i); + memcpy(&aDeviceInfo, dataList[i].service_data, dataList[i].service_data_len); + isChipDevice = true; + break; + } + } + } + __CleanupServiceData(dataList, count); + return isChipDevice; +} + +void ChipDeviceScanner::LeScanResultCb(int result, bt_adapter_le_device_scan_result_info_s * info, void * userData) +{ + ChipDeviceScanner * self = (ChipDeviceScanner *) userData; + chip::Ble::ChipBLEDeviceIdentificationInfo deviceInfo; + + if (!info) + return; + + ChipLogProgress(DeviceLayer, "LE Device Reported!! remote addr [%s]", info->remote_address); + + if (__IsChipThingDevice(info, deviceInfo)) + { + // Looks like a CHIP Thing Device: Service UUID matched + ChipLogProgress(DeviceLayer, "Looks Like Got a CHIP Thing Device: Process further"); + // Report probable CHIP Thing Device to BLEMgrImp class + self->mDelegate->OnChipDeviceScanned(info, deviceInfo); + } + else + ChipLogProgress(DeviceLayer, "Does not Look like a CHIP Device, Skip....."); +} + +gboolean ChipDeviceScanner::TimerExpiredCb(gpointer userData) +{ + ChipDeviceScanner * self = (ChipDeviceScanner *) userData; + ChipLogProgress(DeviceLayer, "Scan Timer expired!!"); + self->StopChipScan(); + return G_SOURCE_REMOVE; +} + +gboolean ChipDeviceScanner::TriggerScan(GMainLoop * mainLoop, gpointer userData) +{ + ChipDeviceScanner * self = (ChipDeviceScanner *) userData; + int ret = BT_ERROR_NONE; + GSource * idleSource; + + self->mAsyncLoop = mainLoop; + + // All set, trigger LE Scan + ret = bt_adapter_le_start_scan(LeScanResultCb, userData); + VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "bt_adapter_le_start_scan() ret: %d", ret)); + ChipLogProgress(DeviceLayer, "Scan started"); + + // Start Timer + idleSource = g_timeout_source_new(kScanTimeout); + g_source_set_callback(idleSource, TimerExpiredCb, userData, nullptr); + g_source_set_priority(idleSource, G_PRIORITY_HIGH_IDLE); + g_source_attach(idleSource, g_main_loop_get_context(self->mAsyncLoop)); + g_source_unref(idleSource); + return true; + +exit: + return false; +} + +static bool __IsScanFilterSupported(void) +{ + // Tizen API: bt_adapter_le_is_scan_filter_supported() is currently internal + // Defaulting to true + return true; +} + +void ChipDeviceScanner::CheckScanFilter(ScanFilterType filterType, ScanFilterData & filterData) +{ + int ret = BT_ERROR_NONE; + + // Scan Filter check + if (!__IsScanFilterSupported()) + return; + + ret = CreateLEScanFilter(filterType, filterData); + VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "Scan Filter Creation Failed! ret: %d Do Normal Scan", ret)); + + ret = RegisterScanFilter(filterType, filterData); + VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "Scan Filter Registration Failed! ret: %d Do Normal Scan", ret)); + return; +exit: + UnRegisterScanFilter(); +} + +CHIP_ERROR ChipDeviceScanner::StartChipScan(unsigned timeoutMs, ScanFilterType filterType, ScanFilterData & filterData) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + ReturnErrorCodeIf(mIsScanning, CHIP_ERROR_INCORRECT_STATE); + + // Scan Filter Setup if supported: silently bypass error & do filterless scan in case of error + CheckScanFilter(filterType, filterData); + + kScanTimeout = timeoutMs; + + // All set to trigger LE Scan + ChipLogProgress(DeviceLayer, "Start CHIP Scan..."); + if (MainLoop::Instance().AsyncRequest(TriggerScan, this) == false) + { + ChipLogError(DeviceLayer, "Failed to trigger Scan..."); + err = CHIP_ERROR_INTERNAL; + goto exit; + } + + mIsScanning = true; // optimistic, to allow all callbacks to check this + return CHIP_NO_ERROR; + +exit: + ChipLogError(DeviceLayer, "Start CHIP Scan could not succeed fully! Stop Scan..."); + StopChipScan(); + UnRegisterScanFilter(); + return err; +} + +CHIP_ERROR ChipDeviceScanner::StopChipScan(void) +{ + int ret = BT_ERROR_NONE; + ReturnErrorCodeIf(!mIsScanning, CHIP_ERROR_INCORRECT_STATE); + + ret = bt_adapter_le_stop_scan(); + ChipLogError(DeviceLayer, "bt_adapter_le_stop_scan() ret: %d", ret); + + g_main_loop_quit(mAsyncLoop); + ChipLogProgress(DeviceLayer, "CHIP Scanner Async Thread Quit Done..Wait for Thread Windup...!"); + + UnRegisterScanFilter(); + + // Report to Impl class + mDelegate->OnChipScanComplete(); + + mIsScanning = false; + + return CHIP_NO_ERROR; +} + +void ChipDeviceScanner::UnRegisterScanFilter(void) +{ + if (mScanFilter) + { + bt_adapter_le_scan_filter_unregister(mScanFilter); + mScanFilter = nullptr; + } +} + +int ChipDeviceScanner::RegisterScanFilter(ScanFilterType filterType, ScanFilterData & filterData) +{ + int ret = BT_ERROR_NONE; + + switch (filterType) + { + case ScanFilterType::kAddress: { + ChipLogProgress(DeviceLayer, "Register Scan filter: Address"); + ret = bt_adapter_le_scan_filter_set_device_address(mScanFilter, filterData.address); + VerifyOrExit(ret == BT_ERROR_NONE, + ChipLogError(DeviceLayer, "bt_adapter_le_scan_filter_set_device_address() failed. ret: %d", ret)); + break; + } + case ScanFilterType::kServiceUUID: { + ChipLogProgress(DeviceLayer, "Register Scan filter: Service UUID"); + ret = bt_adapter_le_scan_filter_set_service_uuid(mScanFilter, filterData.service_uuid); + VerifyOrExit(ret == BT_ERROR_NONE, + ChipLogError(DeviceLayer, "bt_adapter_le_scan_filter_set_service_uuid() failed. ret: %d", ret)); + break; + } + case ScanFilterType::kServiceData: { + ChipLogProgress(DeviceLayer, "Register Scan filter: Service Data"); + ret = bt_adapter_le_scan_filter_set_service_data(mScanFilter, filterData.service_uuid, filterData.service_data, + filterData.service_data_len); + VerifyOrExit(ret == BT_ERROR_NONE, + ChipLogError(DeviceLayer, "bt_adapter_le_scan_filter_set_service_data() failed. ret: %d", ret)); + break; + } + case ScanFilterType::kNoFilter: + default: + return ret; // Without Scan Filter + } + + ret = bt_adapter_le_scan_filter_register(mScanFilter); + VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "bt_adapter_le_scan_filter_register failed(). ret: %d", ret)); + return ret; +exit: + return ret; +} + +int ChipDeviceScanner::CreateLEScanFilter(ScanFilterType filterType, ScanFilterData & filterData) +{ + int ret = BT_ERROR_NONE; + + ret = bt_adapter_le_scan_filter_create(&mScanFilter); + VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "bt_adapter_le_scan_filter_create() Failed. ret: %d", ret)); + ChipLogError(DeviceLayer, "Scan Filter Created Successfully"); +exit: + return ret; +} + +} // namespace Internal +} // namespace DeviceLayer +} // namespace chip + +#endif // CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE diff --git a/src/platform/Tizen/ChipDeviceScanner.h b/src/platform/Tizen/ChipDeviceScanner.h new file mode 100644 index 00000000000000..ea80eb44aa4d2b --- /dev/null +++ b/src/platform/Tizen/ChipDeviceScanner.h @@ -0,0 +1,119 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * Provides an implementation of the Matter Device Scanner + * for the Tizen platform. + */ + +#pragma once + +#include + +#if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE + +#include +#include + +#include +#include +#include + +#include + +namespace chip { +namespace DeviceLayer { +namespace Internal { + +#define CHIP_TIZEN_BLE_ADDRESS_STRING_LEN 18 +#define CHIP_TIZEN_BLE_SERVICE_DATA_MAX_LEN 7 +#define CHIP_TIZEN_BLE_SERVICE_UUID_STRING_LEN 37 + +enum class ScanFilterType +{ + kAddress, // Scan by Address + kServiceUUID, // Scan by UUID + kServiceData, // Scan by Service Data + kNoFilter, // For Filterless Scan +}; + +struct ScanFilterData +{ + char address[CHIP_TIZEN_BLE_ADDRESS_STRING_LEN]; + char service_uuid[CHIP_TIZEN_BLE_SERVICE_UUID_STRING_LEN]; + char service_data[CHIP_TIZEN_BLE_SERVICE_DATA_MAX_LEN]; + int service_data_len; +}; + +// Receives callbacks when chip devices are being scanned +class ChipDeviceScannerDelegate +{ +public: + virtual ~ChipDeviceScannerDelegate(void) {} + + // Called when a CHIP device was found + virtual void OnChipDeviceScanned(void * device, const chip::Ble::ChipBLEDeviceIdentificationInfo & info) = 0; + + // Called when a scan was completed (stopped or timed out) + virtual void OnChipScanComplete(void) = 0; +}; + +/// Allows scanning for CHIP devices +/// +/// Will perform scan operations and call back whenever a device is discovered. +class ChipDeviceScanner +{ +public: + ChipDeviceScanner(void){}; + + /// NOTE: prefer to use the ::Create method instead direct constructor calling. + ChipDeviceScanner(ChipDeviceScannerDelegate * delegate); + + ~ChipDeviceScanner(void); + + /// Initiate a scan for devices, with the given timeout & scan filter data + CHIP_ERROR StartChipScan(unsigned timeoutMs, ScanFilterType filterType, ScanFilterData & filterData); + + /// Stop any currently running scan + CHIP_ERROR StopChipScan(void); + + /// Create a new device scanner + /// Convenience method to allocate any required variables. + static std::unique_ptr Create(ChipDeviceScannerDelegate * delegate); + +private: + static gboolean TriggerScan(GMainLoop * mainLoop, gpointer userData); + static void LeScanResultCb(int result, bt_adapter_le_device_scan_result_info_s * info, void * userData); + void CheckScanFilter(ScanFilterType filterType, ScanFilterData & filterData); + int RegisterScanFilter(ScanFilterType filterType, ScanFilterData & filterData); + void UnRegisterScanFilter(void); + int CreateLEScanFilter(ScanFilterType filterType, ScanFilterData & filterData); + static gboolean TimerExpiredCb(gpointer user_data); + + ChipDeviceScannerDelegate * mDelegate = nullptr; + bool mIsScanning = false; + bool mIsStopping = false; + GMainLoop * mAsyncLoop = nullptr; + bt_scan_filter_h mScanFilter = nullptr; +}; + +} // namespace Internal +} // namespace DeviceLayer +} // namespace chip + +#endif // CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE