From e3333c5bc4a58275ffb7833f07eedb7d59604df0 Mon Sep 17 00:00:00 2001 From: Wootak Jung Date: Thu, 30 Sep 2021 10:24:08 +0900 Subject: [PATCH] [Tizen] Add ble peripheral functionality (#9947) * Add ble peripheral functionality Co-authored-by: Anupam Roy Co-authored-by: Dohyun Pyun * Apply comment Co-authored-by: Anupam Roy Co-authored-by: Dohyun Pyun --- src/platform/Tizen/BLEManagerImpl.cpp | 760 ++++++++++++++++++- src/platform/Tizen/BLEManagerImpl.h | 82 +- src/platform/Tizen/CHIPDevicePlatformEvent.h | 27 +- 3 files changed, 855 insertions(+), 14 deletions(-) diff --git a/src/platform/Tizen/BLEManagerImpl.cpp b/src/platform/Tizen/BLEManagerImpl.cpp index 86c22200b6ada9..6e17b916c073d6 100644 --- a/src/platform/Tizen/BLEManagerImpl.cpp +++ b/src/platform/Tizen/BLEManagerImpl.cpp @@ -28,6 +28,9 @@ #if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE +#include "MainLoop.h" +#include + using namespace ::chip; using namespace ::chip::Ble; @@ -37,35 +40,687 @@ namespace Internal { BLEManagerImpl BLEManagerImpl::sInstance; -CHIP_ERROR BLEManagerImpl::_Init(void) +struct BLEConnection +{ + char * peerAddr; + uint16_t mtu; + bool subscribed; + void * gattCharC1Handle; + void * gattCharC2Handle; + bool isChipDevice; +}; + +/* CHIPoBLE UUID strings */ +const char * chip_ble_service_uuid = "0000FFF6-0000-1000-8000-00805F9B34FB"; +const char * chip_ble_char_c1_tx_uuid = "18EE2EF5-263D-4559-959F-4F9C429F9D11"; +const char * chip_ble_char_c2_rx_uuid = "18EE2EF5-263D-4559-959F-4F9C429F9D12"; +/* CCCD */ +const char * desc_uuid_short = "2902"; + +const char * chip_ble_service_uuid_short = "FFF6"; + +const int BtpServiceDataLenMax = + 7; // OpCode(1) + Discriminator(2) + VendorId(2) + ProductId(2), 5.2.3.8.6. Advertising Data, CHIP Specification + +static void __AdapterStateChangedCb(int result, bt_adapter_state_e adapterState, void * userData) +{ + ChipLogProgress(DeviceLayer, "Adapter State Changed: %s", adapterState == BT_ADAPTER_ENABLED ? "Enabled" : "Disabled"); +} + +void BLEManagerImpl::GattConnectionStateChangedCb(int result, bool connected, const char * remoteAddress, void * userData) +{ + ChipLogProgress(DeviceLayer, "Gatt Connection State Changed: %s result [%d]", connected ? "Connected" : "Disconnected", result); + + sInstance.HandleConnectionEvent(connected, remoteAddress); +} + +gboolean BLEManagerImpl::_BleInitialize(void * userData) +{ + int ret; + + if (sInstance.mFlags.Has(Flags::kTizenBLELayerInitialized)) + { + ChipLogProgress(DeviceLayer, "BLE Already Initialized"); + return true; + } + + ret = bt_initialize(); + VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "bt_initialize() failed. ret: %d", ret)); + + ret = bt_adapter_set_state_changed_cb(__AdapterStateChangedCb, NULL); + VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "bt_adapter_set_state_changed_cb() failed. ret: %d", ret)); + + ret = bt_gatt_server_initialize(); + VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "bt_gatt_server_initialize() failed. ret: %d", ret)); + + ret = bt_gatt_set_connection_state_changed_cb(GattConnectionStateChangedCb, NULL); + VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "bt_adapter_set_state_changed_cb() failed. ret: %d", ret)); + + sInstance.InitConnectionData(); + + sInstance.mFlags.Set(Flags::kTizenBLELayerInitialized); + ChipLogProgress(DeviceLayer, "BLE Initialized"); + return true; + +exit: + return false; +} + +static int __GetAttInfo(bt_gatt_h gattHandle, char ** uuid, int * type) +{ + int ret; + + ret = bt_gatt_get_type(gattHandle, (bt_gatt_type_e *) type); + VerifyOrReturnError(ret == BT_ERROR_NONE, ret); + + return bt_gatt_get_uuid(gattHandle, uuid); +} + +static const char * __ConvertAttTypeToStr(int type) +{ + switch (type) + { + case BT_GATT_TYPE_SERVICE: + return "Service"; + case BT_GATT_TYPE_CHARACTERISTIC: + return "Characteristic"; + case BT_GATT_TYPE_DESCRIPTOR: + return "Descriptor"; + default: + return NULL; + } +} + +static void __ReadValueRequestedCb(const char * remoteAddress, int requestId, bt_gatt_server_h server, bt_gatt_h gattHandle, + int offset, void * userData) +{ + int ret, len = 0, type = 0; + char *value = NULL, *uuid = NULL; + + __GetAttInfo(gattHandle, &uuid, &type); + ChipLogProgress(DeviceLayer, "Read Requested on %s(%s)", __ConvertAttTypeToStr(type), uuid); + g_free(uuid); + + ret = bt_gatt_get_value(gattHandle, &value, &len); + VerifyOrReturn(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "bt_gatt_get_value() failed. ret: %d", ret)); + + ChipLogProgress(DeviceLayer, "Read Value: %s(len: %d)", value, len); + + ret = bt_gatt_server_send_response(requestId, BT_GATT_REQUEST_TYPE_READ, offset, 0x00, value, len); + g_free(value); + VerifyOrReturn(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "bt_gatt_server_send_response() failed. ret: %d", ret)); +} + +void BLEManagerImpl::WriteValueRequestedCb(const char * remoteAddress, int requestId, bt_gatt_server_h server, bt_gatt_h gattHandle, + bool responseNeeded, int offset, const char * value, int len, void * userData) +{ + int ret, type = 0; + char * uuid = NULL; + BLEConnection * conn = nullptr; + + conn = (BLEConnection *) g_hash_table_lookup(sInstance.mConnectionMap, remoteAddress); + VerifyOrReturn(conn != nullptr, ChipLogError(DeviceLayer, "Failed to find connection info")); + + VerifyOrReturn(__GetAttInfo(gattHandle, &uuid, &type) == BT_ERROR_NONE, + ChipLogError(DeviceLayer, "Failed to fetch GATT Attribute from GATT handle")); + + ChipLogProgress(DeviceLayer, "Write Requested on %s(%s)", __ConvertAttTypeToStr(type), uuid); + ChipLogProgress(DeviceLayer, "Write Value: %s(len: %d)", value, len); + g_free(uuid); + + ret = bt_gatt_set_value(gattHandle, value, len); + VerifyOrReturn(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "bt_gatt_set_value() failed. ret: %d", ret)); + + ret = bt_gatt_server_send_response(requestId, BT_GATT_REQUEST_TYPE_WRITE, offset, 0x00, NULL, 0); + VerifyOrReturn(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "bt_gatt_server_send_response() failed. ret: %d", ret)); + + sInstance.HandleC1CharWriteEvent(conn, (const uint8_t *) value, len); +} + +void BLEManagerImpl::NotificationStateChangedCb(bool notify, bt_gatt_server_h server, bt_gatt_h gattHandle, void * userData) +{ + int type = 0; + char * uuid = NULL; + BLEConnection * conn = nullptr; + GHashTableIter iter; + gpointer key, value; + + g_hash_table_iter_init(&iter, sInstance.mConnectionMap); + while (g_hash_table_iter_next(&iter, &key, &value)) + { + /* NOTE: Currently Tizen Platform API does not return remote device address, which enables/disables + * notification/Indication. Therefore, returning first Connection */ + conn = (BLEConnection *) value; + break; + } + + VerifyOrReturn(conn != nullptr, ChipLogError(DeviceLayer, "Failed to find connection info")); + VerifyOrReturn(__GetAttInfo(gattHandle, &uuid, &type) == BT_ERROR_NONE, + ChipLogError(DeviceLayer, "Failed to fetch GATT Attribute from GATT handle")); + + ChipLogProgress(DeviceLayer, "Notification State Changed %d on %s(%s)", notify, __ConvertAttTypeToStr(type), uuid); + g_free(uuid); + sInstance.NotifyBLESubscribed(notify ? true : false, conn); +} + +void BLEManagerImpl::IndicationConfirmationCb(int result, const char * remoteAddress, bt_gatt_server_h server, + bt_gatt_h characteristic, bool completed, void * userData) +{ + BLEConnection * conn = nullptr; + VerifyOrReturn(result == BT_ERROR_NONE, ChipLogError(DeviceLayer, "Failed to Get Indication Confirmation")); + + conn = (BLEConnection *) g_hash_table_lookup(sInstance.mConnectionMap, remoteAddress); + + VerifyOrReturn(conn != nullptr, ChipLogError(DeviceLayer, "Could not find connection for [%s]", remoteAddress)); + VerifyOrReturn(sInstance.mGattCharC2Handle == characteristic, + ChipLogError(DeviceLayer, "Gatt characteristic handle did not match")); + + sInstance.NotifyBLEIndicationConfirmation(conn); +} + +void BLEManagerImpl::AdvertisingStateChangedCb(int result, bt_advertiser_h advertiser, bt_adapter_le_advertising_state_e advState, + void * userData) +{ + ChipLogProgress(DeviceLayer, "Advertising %s", advState == BT_ADAPTER_LE_ADVERTISING_STARTED ? "Started" : "Stopped"); + + if (advState == BT_ADAPTER_LE_ADVERTISING_STARTED) + { + sInstance.mFlags.Set(Flags::kAdvertising); + sInstance.NotifyBLEPeripheralAdvStartComplete(true, nullptr); + } + else + { + sInstance.mFlags.Clear(Flags::kAdvertising); + sInstance.NotifyBLEPeripheralAdvStopComplete(true, nullptr); + } + + if (sInstance.mFlags.Has(Flags::kAdvertisingRefreshNeeded)) + { + sInstance.mFlags.Clear(Flags::kAdvertisingRefreshNeeded); + PlatformMgr().ScheduleWork(DriveBLEState, 0); + } + + sInstance.mAdvReqInProgress = false; +} + +// ====== Private Functions. +void BLEManagerImpl::NotifyBLEPeripheralGATTServerRegisterComplete(bool aIsSuccess, void * apAppstate) +{ + ChipDeviceEvent event; + event.Type = DeviceEventType::kPlatformTizenBLEPeripheralGATTServerRegisterComplete; + event.Platform.BLEPeripheralGATTServerRegisterComplete.mIsSuccess = aIsSuccess; + event.Platform.BLEPeripheralGATTServerRegisterComplete.mpAppstate = apAppstate; + PlatformMgr().PostEventOrDie(&event); +} + +void BLEManagerImpl::NotifyBLEPeripheralAdvConfiguredComplete(bool aIsSuccess, void * apAppstate) +{ + ChipDeviceEvent event; + event.Type = DeviceEventType::kPlatformTizenBLEPeripheralAdvConfiguredComplete; + event.Platform.BLEPeripheralAdvConfiguredComplete.mIsSuccess = aIsSuccess; + event.Platform.BLEPeripheralAdvConfiguredComplete.mpAppstate = apAppstate; + PlatformMgr().PostEventOrDie(&event); +} + +void BLEManagerImpl::NotifyBLEPeripheralAdvStartComplete(bool aIsSuccess, void * apAppstate) +{ + ChipDeviceEvent event; + event.Type = DeviceEventType::kPlatformTizenBLEPeripheralAdvStartComplete; + event.Platform.BLEPeripheralAdvStartComplete.mIsSuccess = aIsSuccess; + event.Platform.BLEPeripheralAdvStartComplete.mpAppstate = apAppstate; + PlatformMgr().PostEventOrDie(&event); +} + +void BLEManagerImpl::NotifyBLEPeripheralAdvStopComplete(bool aIsSuccess, void * apAppstate) +{ + ChipDeviceEvent event; + event.Type = DeviceEventType::kPlatformTizenBLEPeripheralAdvStopComplete; + event.Platform.BLEPeripheralAdvStopComplete.mIsSuccess = aIsSuccess; + event.Platform.BLEPeripheralAdvStopComplete.mpAppstate = apAppstate; + PlatformMgr().PostEventOrDie(&event); +} + +void BLEManagerImpl::NotifyBLEWriteReceived(System::PacketBufferHandle & buf, BLE_CONNECTION_OBJECT conId) +{ + ChipDeviceEvent event; + event.Type = DeviceEventType::kCHIPoBLEWriteReceived; + event.CHIPoBLEWriteReceived.ConId = conId; + event.CHIPoBLEWriteReceived.Data = std::move(buf).UnsafeRelease(); + PlatformMgr().PostEventOrDie(&event); +} + +void BLEManagerImpl::NotifyBLESubscribed(bool indicationsEnabled, BLE_CONNECTION_OBJECT conId) +{ + ChipDeviceEvent event; + event.Type = (indicationsEnabled) ? DeviceEventType::kCHIPoBLESubscribe : DeviceEventType::kCHIPoBLEUnsubscribe; + event.CHIPoBLESubscribe.ConId = conId; + PlatformMgr().PostEventOrDie(&event); +} + +void BLEManagerImpl::NotifyBLEIndicationConfirmation(BLE_CONNECTION_OBJECT conId) +{ + ChipDeviceEvent event; + event.Type = DeviceEventType::kCHIPoBLEIndicateConfirm; + event.CHIPoBLEIndicateConfirm.ConId = conId; + PlatformMgr().PostEventOrDie(&event); +} + +void BLEManagerImpl::NotifyBLEConnectionEstablished(BLE_CONNECTION_OBJECT conId, CHIP_ERROR error) +{ + ChipDeviceEvent connectionEvent; + connectionEvent.Type = DeviceEventType::kCHIPoBLEConnectionEstablished; + PlatformMgr().PostEventOrDie(&connectionEvent); +} + +void BLEManagerImpl::NotifyBLEDisconnection(BLE_CONNECTION_OBJECT conId, CHIP_ERROR error) +{ + ChipDeviceEvent event; + event.Type = DeviceEventType::kCHIPoBLEConnectionError; + event.CHIPoBLEConnectionError.ConId = conId; + event.CHIPoBLEConnectionError.Reason = error; + PlatformMgr().PostEventOrDie(&event); +} + +int BLEManagerImpl::RegisterGATTServer() +{ + int ret = BT_ERROR_NONE; + + bt_gatt_server_h server = NULL; + bt_gatt_h service = NULL; + bt_gatt_h char1 = NULL, char2 = NULL; + bt_gatt_h desc = NULL; + char desc_value[2] = { 0, 0 }; + + ChipLogProgress(DeviceLayer, "Start GATT Service Registration"); + + // Create Server + ret = bt_gatt_server_create(&server); + VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "bt_gatt_server_create() failed. ret: %d", ret)); + + // Create Service (BTP Service) + ret = bt_gatt_service_create(chip_ble_service_uuid, BT_GATT_SERVICE_TYPE_PRIMARY, &service); + VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "bt_gatt_service_create() failed. ret: %d", ret)); + + // Create 1st Characteristic (Client TX Buffer) + ret = bt_gatt_characteristic_create( + chip_ble_char_c1_tx_uuid, BT_GATT_PERMISSION_WRITE, + BT_GATT_PROPERTY_WRITE, // Write Request is not coming if we use WITHOUT_RESPONSE property. Let's use WRITE property and + // consider to use WITHOUT_RESPONSE property in the future according to the CHIP Spec 4.16.3.2. BTP + // GATT Service + "CHIPoBLE_C1", strlen("CHIPoBLE_C1"), &char1); + + VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "bt_gatt_characteristic_create() failed. ret: %d", ret)); + ret = bt_gatt_server_set_write_value_requested_cb(char1, WriteValueRequestedCb, NULL); + VerifyOrExit(ret == BT_ERROR_NONE, + ChipLogError(DeviceLayer, "bt_gatt_server_set_write_value_requested_cb() failed. ret: %d", ret)); + ret = bt_gatt_service_add_characteristic(service, char1); + VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "bt_gatt_service_add_characteristic() failed. ret: %d", ret)); + + // Create 2nd Characteristic (Client RX Buffer) + ret = bt_gatt_characteristic_create(chip_ble_char_c2_rx_uuid, BT_GATT_PERMISSION_READ, + BT_GATT_PROPERTY_READ | BT_GATT_PROPERTY_INDICATE, "CHIPoBLE_C2", strlen("CHIPoBLE_C2"), + &char2); + + VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "bt_gatt_characteristic_create() failed. ret: %d", ret)); + ret = bt_gatt_server_set_read_value_requested_cb(char2, __ReadValueRequestedCb, NULL); + VerifyOrExit(ret == BT_ERROR_NONE, + ChipLogError(DeviceLayer, "bt_gatt_server_set_read_value_requested_cb() failed. ret: %d", ret)); + ret = bt_gatt_server_set_characteristic_notification_state_change_cb(char2, NotificationStateChangedCb, NULL); + VerifyOrExit( + ret == BT_ERROR_NONE, + ChipLogError(DeviceLayer, "bt_gatt_server_set_characteristic_notification_state_change_cb() failed. ret: %d", ret)); + + // Create CCC Descriptor + ret = bt_gatt_descriptor_create(desc_uuid_short, BT_GATT_PERMISSION_READ | BT_GATT_PERMISSION_WRITE, desc_value, + sizeof(desc_value), &desc); + VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "bt_gatt_descriptor_create() failed. ret: %d", ret)); + ret = bt_gatt_characteristic_add_descriptor(char2, desc); + VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "bt_gatt_characteristic_add_descriptor() failed. ret: %d", ret)); + ret = bt_gatt_service_add_characteristic(service, char2); + VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "bt_gatt_service_add_characteristic() failed. ret: %d", ret)); + + // Register Service to Server + ret = bt_gatt_server_register_service(server, service); + VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "bt_gatt_server_register_service() failed. ret: %d", ret)); + + // Start Server + ret = bt_gatt_server_start(); + VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "bt_gatt_server_start() failed. ret: %d", ret)); + + ChipLogDetail(DeviceLayer, "NotifyBLEPeripheralGATTServerRegisterComplete Success"); + BLEManagerImpl::NotifyBLEPeripheralGATTServerRegisterComplete(true, nullptr); + + // Save the Local Peripheral char1 & char2 handles + mGattCharC1Handle = char1; + mGattCharC2Handle = char2; + + return ret; + +exit: + ChipLogDetail(DeviceLayer, "NotifyBLEPeripheralGATTServerRegisterComplete Failed"); + BLEManagerImpl::NotifyBLEPeripheralGATTServerRegisterComplete(false, nullptr); + return ret; +} + +int BLEManagerImpl::StartAdvertising() +{ + int ret = BT_ERROR_NONE; + CHIP_ERROR err = CHIP_NO_ERROR; + char service_data[BtpServiceDataLenMax] = { + 0x0, + }; // need to fill advertising data. 5.2.3.8.6. Advertising Data, CHIP Specification + ChipBLEDeviceIdentificationInfo deviceIdInfo = { + 0x0, + }; + + if (sInstance.mAdvReqInProgress) + { + ChipLogProgress(DeviceLayer, "Advertising Request In Progress"); + return ret; + } + + ChipLogProgress(DeviceLayer, "Start Advertising"); + + if (mAdvertiser == NULL) + { + ret = bt_adapter_le_create_advertiser(&mAdvertiser); + VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "bt_adapter_le_create_advertiser() failed. ret: %d", ret)); + } + else + { + ret = bt_adapter_le_clear_advertising_data(mAdvertiser, BT_ADAPTER_LE_PACKET_ADVERTISING); + VerifyOrExit(ret == BT_ERROR_NONE, + ChipLogError(DeviceLayer, "bt_adapter_le_clear_advertising_data() failed. ret: %d", ret)); + + ret = bt_adapter_le_clear_advertising_data(mAdvertiser, BT_ADAPTER_LE_PACKET_SCAN_RESPONSE); + VerifyOrExit(ret == BT_ERROR_NONE, + ChipLogError(DeviceLayer, "bt_adapter_le_clear_advertising_data() failed. ret: %d", ret)); + } + + if (mFlags.Has(Flags::kFastAdvertisingEnabled)) + { + ret = bt_adapter_le_set_advertising_mode(mAdvertiser, BT_ADAPTER_LE_ADVERTISING_MODE_LOW_LATENCY); + VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "bt_adapter_le_set_advertising_mode() failed. ret: %d", ret)); + + // NOTE: Check specification for recommended Advertising Interval range for Fast Advertising + // ret = bt_adapter_le_set_advertising_interval(mAdvertiser, BT_LE_ADV_INTERVAL_MIN, BT_LE_ADV_INTERVAL_MIN); + } + else + { + // NOTE: Check specification for recommended Advertising Interval range for Slow Advertising + // ret = bt_adapter_le_set_advertising_interval(mAdvertiser, BT_LE_ADV_INTERVAL_MAX, BT_LE_ADV_INTERVAL_MAX); + } + + err = ConfigurationMgr().GetBLEDeviceIdentificationInfo(deviceIdInfo); + VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(DeviceLayer, "GetBLEDeviceIdentificationInfo() failed. %s", ErrorStr(err))); + + memcpy(service_data, &deviceIdInfo, sizeof(service_data)); + ret = bt_adapter_le_add_advertising_service_data(mAdvertiser, BT_ADAPTER_LE_PACKET_ADVERTISING, chip_ble_service_uuid_short, + service_data, sizeof(service_data)); + VerifyOrExit(ret == BT_ERROR_NONE, + ChipLogError(DeviceLayer, "bt_adapter_le_add_advertising_service_data() failed. ret: %d", ret)); + + BLEManagerImpl::NotifyBLEPeripheralAdvConfiguredComplete(true, nullptr); + + ret = bt_adapter_le_start_advertising_new(mAdvertiser, AdvertisingStateChangedCb, NULL); + VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "bt_adapter_le_start_advertising_new() failed. ret: %d", ret)); + sInstance.mAdvReqInProgress = true; + return ret; + +exit: + BLEManagerImpl::NotifyBLEPeripheralAdvStartComplete(false, nullptr); + return ret; +} + +int BLEManagerImpl::StopAdvertising() +{ + int ret = BT_ERROR_NONE; + + ChipLogProgress(DeviceLayer, "Stop Advertising"); + ret = bt_adapter_le_stop_advertising(mAdvertiser); + VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "bt_adapter_le_stop_advertising() failed. ret: %d", ret)); + sInstance.mAdvReqInProgress = true; + return ret; + +exit: + BLEManagerImpl::NotifyBLEPeripheralAdvStopComplete(false, nullptr); + return ret; +} + +void BLEManagerImpl::InitConnectionData(void) +{ + /* Initialize Hashmap */ + if (!mConnectionMap) + { + mConnectionMap = g_hash_table_new(g_str_hash, g_str_equal); + ChipLogProgress(DeviceLayer, "GATT Connection HashMap created"); + } +} + +void BLEManagerImpl::AddConnectionData(const char * remoteAddr) +{ + BLEConnection * conn; + ChipLogProgress(DeviceLayer, "AddConnectionData for [%s]", remoteAddr); + + if (!g_hash_table_lookup(mConnectionMap, 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); + } +} + +void BLEManagerImpl::RemoveConnectionData(const char * remoteAddr) +{ + BLEConnection * conn = nullptr; + + conn = (BLEConnection *) g_hash_table_lookup(mConnectionMap, remoteAddr); + if (!conn) + return; + + g_hash_table_remove(mConnectionMap, remoteAddr); + + g_free(conn->peerAddr); + g_free(conn); + + ChipLogProgress(DeviceLayer, "Connection Removed for [%s]", remoteAddr); +} + +void BLEManagerImpl::HandleC1CharWriteEvent(BLE_CONNECTION_OBJECT conId, const uint8_t * value, size_t len) { CHIP_ERROR err = CHIP_NO_ERROR; + System::PacketBufferHandle buf; + + ChipLogProgress(DeviceLayer, "Write request received for CHIPoBLE Client TX 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); + NotifyBLEWriteReceived(buf, conId); + return; +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(DeviceLayer, "HandleC1CharWriteEvent() failed: %s", ErrorStr(err)); + } +} + +void BLEManagerImpl::HandleConnectionEvent(bool connected, const char * remoteAddress) +{ + if (connected) + { + ChipLogProgress(DeviceLayer, "Device Connected [%s]", remoteAddress); + AddConnectionData(remoteAddress); + } + 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); + } +} + +void BLEManagerImpl::DriveBLEState() +{ + int ret = BT_ERROR_NONE; + + ChipLogProgress(DeviceLayer, "Enter DriveBLEState"); + + if (!mIsCentral && mServiceMode == ConnectivityManager::kCHIPoBLEServiceMode_Enabled && !mFlags.Has(Flags::kAppRegistered)) + { + ret = RegisterGATTServer(); + VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "Register GATT Server Failed. ret: %d", ret)); + + ChipLogProgress(DeviceLayer, "GATT Service Registered"); + mFlags.Set(Flags::kAppRegistered); + ExitNow(); + } + + if (mServiceMode == ConnectivityManager::kCHIPoBLEServiceMode_Enabled && mFlags.Has(Flags::kAdvertisingEnabled)) + { + if (!mFlags.Has(Flags::kAdvertising)) + { + ret = StartAdvertising(); + VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "Start Advertising Failed. ret: %d", ret)); + } + else if (mFlags.Has(Flags::kAdvertisingRefreshNeeded)) + { + ChipLogProgress(DeviceLayer, "Advertising Refreshed Needed. Stop Advertising"); + ret = StopAdvertising(); + VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "Stop Advertising Failed. ret: %d", ret)); + } + } + else if (mFlags.Has(Flags::kAdvertising)) + { + ChipLogProgress(DeviceLayer, "Stop Advertising"); + + ret = StopAdvertising(); + VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "Stop Advertising Failed. ret: %d", ret)); + + ret = bt_adapter_le_destroy_advertiser(mAdvertiser); + VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "bt_adapter_le_destroy_advertiser() failed. ret: %d", ret)); + mAdvertiser = NULL; + } + +exit: + if (ret != BT_ERROR_NONE) + { + mServiceMode = ConnectivityManager::kCHIPoBLEServiceMode_Disabled; + } +} + +void BLEManagerImpl::DriveBLEState(intptr_t arg) +{ + sInstance.DriveBLEState(); +} + +CHIP_ERROR BLEManagerImpl::_Init(void) +{ + CHIP_ERROR err; + bool ret; + + err = BleLayer::Init(this, this, this, &DeviceLayer::SystemLayer()); + SuccessOrExit(err); + + mServiceMode = ConnectivityManager::kCHIPoBLEServiceMode_Enabled; + + ChipLogProgress(DeviceLayer, "Initialize BLE"); + ret = MainLoop::Instance().Init(_BleInitialize); + VerifyOrExit(ret != false, err = CHIP_ERROR_INTERNAL); + + PlatformMgr().ScheduleWork(DriveBLEState, 0); + +exit: return err; } CHIP_ERROR BLEManagerImpl::_SetCHIPoBLEServiceMode(CHIPoBLEServiceMode val) { - return CHIP_ERROR_NOT_IMPLEMENTED; + CHIP_ERROR err = CHIP_NO_ERROR; + + VerifyOrExit(val != ConnectivityManager::kCHIPoBLEServiceMode_NotSupported, err = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(mServiceMode == ConnectivityManager::kCHIPoBLEServiceMode_NotSupported, err = CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); + + if (val != mServiceMode) + { + mServiceMode = val; + PlatformMgr().ScheduleWork(DriveBLEState, 0); + } + +exit: + return err; } CHIP_ERROR BLEManagerImpl::_SetAdvertisingEnabled(bool val) { - return CHIP_ERROR_NOT_IMPLEMENTED; + mFlags.Set(Flags::kAdvertisingEnabled, val); + PlatformMgr().ScheduleWork(DriveBLEState, 0); + return CHIP_NO_ERROR; } CHIP_ERROR BLEManagerImpl::_SetAdvertisingMode(BLEAdvertisingMode mode) { - return CHIP_ERROR_NOT_IMPLEMENTED; + switch (mode) + { + case BLEAdvertisingMode::kFastAdvertising: + mFlags.Set(Flags::kFastAdvertisingEnabled, true); + break; + case BLEAdvertisingMode::kSlowAdvertising: + mFlags.Set(Flags::kFastAdvertisingEnabled, false); + break; + default: + return CHIP_ERROR_INVALID_ARGUMENT; + } + mFlags.Set(Flags::kAdvertisingRefreshNeeded); + PlatformMgr().ScheduleWork(DriveBLEState, 0); + return CHIP_NO_ERROR; } CHIP_ERROR BLEManagerImpl::_GetDeviceName(char * buf, size_t bufSize) { - return CHIP_ERROR_NOT_IMPLEMENTED; + CHIP_ERROR err = CHIP_NO_ERROR; + int ret; + char * deviceName = NULL; + + VerifyOrExit(buf != NULL, err = CHIP_ERROR_INVALID_ARGUMENT); + + ret = bt_adapter_get_name(&deviceName); + if (ret != BT_ERROR_NONE) + { + ChipLogError(DeviceLayer, "bt_adapter_get_name() failed. ret: %d", ret); + return CHIP_ERROR_INTERNAL; + } + + VerifyOrExit(deviceName != NULL, err = CHIP_ERROR_INTERNAL); + VerifyOrExit(strlen(deviceName) >= bufSize, err = CHIP_ERROR_BUFFER_TOO_SMALL); + + g_strlcpy(buf, deviceName, bufSize); + ChipLogProgress(DeviceLayer, "DeviceName: %s", buf); + +exit: + return err; } CHIP_ERROR BLEManagerImpl::_SetDeviceName(const char * deviceName) { - return CHIP_ERROR_NOT_IMPLEMENTED; + CHIP_ERROR err = CHIP_NO_ERROR; + int ret; + + VerifyOrExit(deviceName != NULL, err = CHIP_ERROR_INVALID_ARGUMENT); + + ret = bt_adapter_set_name(deviceName); + if (ret != BT_ERROR_NONE) + { + ChipLogError(DeviceLayer, "bt_adapter_set_name() failed. ret: %d", ret); + return CHIP_ERROR_INTERNAL; + } + +exit: + return err; } uint16_t BLEManagerImpl::_NumConnections(void) @@ -75,10 +730,72 @@ uint16_t BLEManagerImpl::_NumConnections(void) CHIP_ERROR BLEManagerImpl::ConfigureBle(uint32_t aAdapterId, bool aIsCentral) { - return CHIP_ERROR_NOT_IMPLEMENTED; + mAdapterId = aAdapterId; + mIsCentral = aIsCentral; + return CHIP_NO_ERROR; } -void BLEManagerImpl::_OnPlatformEvent(const ChipDeviceEvent * event) {} +void BLEManagerImpl::HandlePlatformSpecificBLEEvent(const ChipDeviceEvent * apEvent) +{ + ChipLogDetail(DeviceLayer, "HandlePlatformSpecificBLEEvent %d", apEvent->Type); + + // TODO: Need to implement Tizen Platform Specific events: CHIPDevicePlatformEvent +} + +void BLEManagerImpl::_OnPlatformEvent(const ChipDeviceEvent * event) +{ + ChipBleUUID service_uuid; + ChipBleUUID char_notification_uuid; + ChipBleUUID char_write_uuid; + + switch (event->Type) + { + case DeviceEventType::kCHIPoBLESubscribe: + ChipLogProgress(DeviceLayer, "CHIPoBLESubscribe"); + + StringToUUID(chip_ble_service_uuid, service_uuid); + StringToUUID(chip_ble_char_c2_rx_uuid, char_notification_uuid); + + HandleSubscribeReceived(event->CHIPoBLESubscribe.ConId, &service_uuid, &char_notification_uuid); + NotifyBLEConnectionEstablished(event->CHIPoBLESubscribe.ConId, CHIP_NO_ERROR); + break; + case DeviceEventType::kCHIPoBLEUnsubscribe: + ChipLogProgress(DeviceLayer, "CHIPoBLEUnsubscribe"); + + StringToUUID(chip_ble_service_uuid, service_uuid); + StringToUUID(chip_ble_char_c2_rx_uuid, char_notification_uuid); + + HandleUnsubscribeReceived(event->CHIPoBLESubscribe.ConId, &service_uuid, &char_notification_uuid); + break; + case DeviceEventType::kCHIPoBLEWriteReceived: + ChipLogProgress(DeviceLayer, "CHIPoBLEWriteReceived"); + + StringToUUID(chip_ble_service_uuid, service_uuid); + StringToUUID(chip_ble_char_c1_tx_uuid, char_write_uuid); + HandleWriteReceived(event->CHIPoBLEWriteReceived.ConId, &service_uuid, &char_write_uuid, + PacketBufferHandle::Adopt(event->CHIPoBLEWriteReceived.Data)); + break; + case DeviceEventType::kCHIPoBLEIndicateConfirm: + ChipLogProgress(DeviceLayer, "CHIPoBLEIndicateConfirm"); + + StringToUUID(chip_ble_service_uuid, service_uuid); + StringToUUID(chip_ble_char_c2_rx_uuid, char_notification_uuid); + HandleIndicationConfirmation(event->CHIPoBLEIndicateConfirm.ConId, &service_uuid, &char_notification_uuid); + break; + case DeviceEventType::kCHIPoBLEConnectionError: + ChipLogProgress(DeviceLayer, "CHIPoBLEConnectionError"); + + HandleConnectionError(event->CHIPoBLEConnectionError.ConId, event->CHIPoBLEConnectionError.Reason); + break; + case DeviceEventType::kFabricMembershipChange: + case DeviceEventType::kServiceProvisioningChange: + case DeviceEventType::kAccountPairingChange: + break; + default: + HandlePlatformSpecificBLEEvent(event); + break; + } +} uint16_t BLEManagerImpl::GetMTU(BLE_CONNECTION_OBJECT conId) const { @@ -103,6 +820,26 @@ bool BLEManagerImpl::CloseConnection(BLE_CONNECTION_OBJECT conId) bool BLEManagerImpl::SendIndication(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const Ble::ChipBleUUID * charId, chip::System::PacketBufferHandle pBuf) { + int ret = BT_ERROR_NONE; + BLEConnection * conn = nullptr; + + ChipLogProgress(DeviceLayer, "SendIndication"); + + conn = (BLEConnection *) g_hash_table_lookup(sInstance.mConnectionMap, ((BLEConnection *) conId)->peerAddr); + VerifyOrExit(conn != nullptr, ChipLogError(DeviceLayer, "Failed to find connection info")); + + ret = bt_gatt_set_value(mGattCharC2Handle, (const char *) pBuf->Start(), pBuf->DataLength()); + VerifyOrExit(ret == BT_ERROR_NONE, ChipLogError(DeviceLayer, "bt_gatt_set_value() failed. ret: %d", ret)); + + ChipLogProgress(DeviceLayer, "Sending indication for CHIPoBLE RX characteristic (con %s, len %u)", conn->peerAddr, + pBuf->DataLength()); + + ret = bt_gatt_server_notify_characteristic_changed_value(mGattCharC2Handle, IndicationConfirmationCb, conn->peerAddr, NULL); + VerifyOrExit(ret == BT_ERROR_NONE, + ChipLogError(DeviceLayer, "bt_gatt_server_notify_characteristic_changed_value() failed. ret: %d", ret)); + return true; + +exit: return false; } @@ -126,6 +863,13 @@ 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) {} + +CHIP_ERROR BLEManagerImpl::CancelConnection() +{ + return CHIP_ERROR_NOT_IMPLEMENTED; +} + } // namespace Internal } // namespace DeviceLayer } // namespace chip diff --git a/src/platform/Tizen/BLEManagerImpl.h b/src/platform/Tizen/BLEManagerImpl.h index 8db46b595c0288..3d5ea4f03e43e7 100644 --- a/src/platform/Tizen/BLEManagerImpl.h +++ b/src/platform/Tizen/BLEManagerImpl.h @@ -25,6 +25,10 @@ #if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE +#include +#include +#include + namespace chip { namespace DeviceLayer { namespace Internal { @@ -37,7 +41,8 @@ using namespace chip::Ble; class BLEManagerImpl final : public BLEManager, private Ble::BleLayer, private Ble::BlePlatformDelegate, - private Ble::BleApplicationDelegate + private Ble::BleApplicationDelegate, + private Ble::BleConnectionDelegate { // Allow the BLEManager interface class to delegate method calls to // the implementation methods provided by this class. @@ -60,6 +65,7 @@ class BLEManagerImpl final : public BLEManager, CHIP_ERROR _SetDeviceName(const char * deviceName); uint16_t _NumConnections(void); void _OnPlatformEvent(const ChipDeviceEvent * event); + void HandlePlatformSpecificBLEEvent(const ChipDeviceEvent * event); BleLayer * _GetBleLayer(void); // ===== Members that implement virtual methods on BlePlatformDelegate. @@ -83,12 +89,80 @@ class BLEManagerImpl final : public BLEManager, void NotifyChipConnectionClosed(BLE_CONNECTION_OBJECT conId) override; + // ===== Members that implement virtual methods on BleConnectionDelegate. + + void NewConnection(BleLayer * bleLayer, void * appState, uint16_t connDiscriminator) override; + CHIP_ERROR CancelConnection() override; + // ===== Members for internal use by the following friends. friend BLEManager & BLEMgr(void); friend BLEManagerImpl & BLEMgrImpl(void); static BLEManagerImpl sInstance; + + // ===== Private members reserved for use by this class only. + enum class Flags : uint16_t + { + kAsyncInitCompleted = 0x0001, /**< One-time asynchronous initialization actions have been performed. */ + kTizenBLELayerInitialized = 0x0002, /**< The Tizen Platform BLE layer has been initialized. */ + kAppRegistered = 0x0004, /**< The CHIPoBLE application has been registered with the Bluez layer. */ + kAdvertisingConfigured = 0x0008, /**< CHIPoBLE advertising has been configured in the Bluez layer. */ + kAdvertising = 0x0010, /**< The system is currently CHIPoBLE advertising. */ + kControlOpInProgress = 0x0020, /**< An async control operation has been issued to the ESP BLE layer. */ + kAdvertisingEnabled = 0x0040, /**< The application has enabled CHIPoBLE advertising. */ + kFastAdvertisingEnabled = 0x0080, /**< The application has enabled fast advertising. */ + kUseCustomDeviceName = 0x0100, /**< The application has configured a custom BLE device name. */ + kAdvertisingRefreshNeeded = 0x0200, /**< The advertising configuration/state in BLE layer needs to be updated. */ + }; + + static gboolean _BleInitialize(void * userData); + void DriveBLEState(); + static void DriveBLEState(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); + static void WriteValueRequestedCb(const char * remoteAddress, int requestId, bt_gatt_server_h server, bt_gatt_h gattHandle, + bool responseNeeded, int offset, const char * value, int len, void * userData); + static void IndicationConfirmationCb(int result, const char * remoteAddress, bt_gatt_server_h server, bt_gatt_h characteristic, + bool completed, void * userData); + 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); + + // ==== Connection. + void InitConnectionData(void); + void AddConnectionData(const char * remoteAddr); + void RemoveConnectionData(const char * remoteAddr); + + void HandleC1CharWriteEvent(BLE_CONNECTION_OBJECT conId, const uint8_t * value, size_t len); + void HandleConnectionEvent(bool connected, const char * remoteAddress); + + // ==== BLE Adv & GATT Server. + void NotifyBLEPeripheralGATTServerRegisterComplete(bool aIsSuccess, void * apAppstate); + void NotifyBLEPeripheralAdvConfiguredComplete(bool aIsSuccess, void * apAppstate); + void NotifyBLEPeripheralAdvStartComplete(bool aIsSuccess, void * apAppstate); + void NotifyBLEPeripheralAdvStopComplete(bool aIsSuccess, void * apAppstate); + void NotifyBLESubscribed(bool indicationsEnabled, BLE_CONNECTION_OBJECT conId); + void NotifyBLEIndicationConfirmation(BLE_CONNECTION_OBJECT conId); + void NotifyBLEWriteReceived(System::PacketBufferHandle & buf, BLE_CONNECTION_OBJECT conId); + void NotifyBLEConnectionEstablished(BLE_CONNECTION_OBJECT conId, CHIP_ERROR error); + void NotifyBLEDisconnection(BLE_CONNECTION_OBJECT conId, CHIP_ERROR error); + + int RegisterGATTServer(void); + int StartAdvertising(void); + int StopAdvertising(void); + + CHIPoBLEServiceMode mServiceMode; + BitFlags mFlags; + bool mIsCentral = false; + void * mGattCharC1Handle = nullptr; + void * mGattCharC2Handle = nullptr; + uint32_t mAdapterId; + bt_advertiser_h mAdvertiser = nullptr; + bool mAdvReqInProgress = false; + /* Connection Hash Table Map */ + GHashTable * mConnectionMap = nullptr; }; /** @@ -120,17 +194,17 @@ inline Ble::BleLayer * BLEManagerImpl::_GetBleLayer() inline BLEManager::CHIPoBLEServiceMode BLEManagerImpl::_GetCHIPoBLEServiceMode() { - return ConnectivityManager::kCHIPoBLEServiceMode_NotSupported; + return mServiceMode; } inline bool BLEManagerImpl::_IsAdvertisingEnabled() { - return false; + return mFlags.Has(Flags::kAdvertisingEnabled); } inline bool BLEManagerImpl::_IsAdvertising() { - return false; + return mFlags.Has(Flags::kAdvertising); } } // namespace Internal diff --git a/src/platform/Tizen/CHIPDevicePlatformEvent.h b/src/platform/Tizen/CHIPDevicePlatformEvent.h index 26deed4ca3a96c..c08ca4ed9e77b4 100644 --- a/src/platform/Tizen/CHIPDevicePlatformEvent.h +++ b/src/platform/Tizen/CHIPDevicePlatformEvent.h @@ -43,7 +43,11 @@ enum PublicPlatformSpecificEventTypes */ enum InternalPlatformSpecificEventTypes { - /* None currently defined */ + kPlatformTizenEvent = kRange_InternalPlatformSpecific, + kPlatformTizenBLEPeripheralGATTServerRegisterComplete, + kPlatformTizenBLEPeripheralAdvConfiguredComplete, + kPlatformTizenBLEPeripheralAdvStartComplete, + kPlatformTizenBLEPeripheralAdvStopComplete }; } // namespace DeviceEventType @@ -55,7 +59,26 @@ struct ChipDevicePlatformEvent { union { - /* None currently defined */ + struct + { + bool mIsSuccess; + void * mpAppstate; + } BLEPeripheralGATTServerRegisterComplete; + struct + { + bool mIsSuccess; + void * mpAppstate; + } BLEPeripheralAdvConfiguredComplete; + struct + { + bool mIsSuccess; + void * mpAppstate; + } BLEPeripheralAdvStartComplete; + struct + { + bool mIsSuccess; + void * mpAppstate; + } BLEPeripheralAdvStopComplete; }; };