| /* |
| * |
| * Copyright (c) 2020-2021 Project CHIP Authors |
| * All rights reserved. |
| * |
| * 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 BLEManager singleton object |
| * for the ESP32 (NimBLE) platform. |
| */ |
| /* this file behaves like a config.h, comes first */ |
| #include <platform/internal/CHIPDeviceLayerInternal.h> |
| |
| #if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE |
| |
| #include "sdkconfig.h" |
| |
| #if CONFIG_BT_NIMBLE_ENABLED |
| |
| #include <ble/CHIPBleServiceData.h> |
| #include <lib/support/CodeUtils.h> |
| #include <lib/support/logging/CHIPLogging.h> |
| #include <platform/internal/BLEManager.h> |
| #include <setup_payload/AdditionalDataPayloadGenerator.h> |
| #include <system/SystemTimer.h> |
| |
| #include "esp_log.h" |
| #include "esp_nimble_hci.h" |
| #include "host/ble_hs.h" |
| #include "host/ble_hs_pvcy.h" |
| #include "host/ble_uuid.h" |
| #include "host/util/util.h" |
| #include "nimble/nimble_port.h" |
| #include "nimble/nimble_port_freertos.h" |
| #include "services/gap/ble_svc_gap.h" |
| #include "services/gatt/ble_svc_gatt.h" |
| |
| #define MAX_ADV_DATA_LEN 31 |
| #define CHIP_ADV_DATA_TYPE_FLAGS 0x01 |
| #define CHIP_ADV_DATA_FLAGS 0x06 |
| #define CHIP_ADV_DATA_TYPE_SERVICE_DATA 0x16 |
| |
| using namespace ::chip; |
| using namespace ::chip::Ble; |
| |
| namespace chip { |
| namespace DeviceLayer { |
| namespace Internal { |
| |
| namespace { |
| |
| struct ESP32ChipServiceData |
| { |
| uint8_t ServiceUUID[2]; |
| ChipBLEDeviceIdentificationInfo DeviceIdInfo; |
| }; |
| |
| const ble_uuid16_t ShortUUID_CHIPoBLEService = { BLE_UUID_TYPE_16, 0xFFF6 }; |
| |
| const ble_uuid128_t UUID128_CHIPoBLEChar_RX = { |
| BLE_UUID_TYPE_128, { 0x11, 0x9D, 0x9F, 0x42, 0x9C, 0x4F, 0x9F, 0x95, 0x59, 0x45, 0x3D, 0x26, 0xF5, 0x2E, 0xEE, 0x18 } |
| }; |
| const ChipBleUUID chipUUID_CHIPoBLEChar_RX = { { 0x18, 0xEE, 0x2E, 0xF5, 0x26, 0x3D, 0x45, 0x59, 0x95, 0x9F, 0x4F, 0x9C, 0x42, 0x9F, |
| 0x9D, 0x11 } }; |
| |
| const ChipBleUUID chipUUID_CHIPoBLEChar_TX = { { 0x18, 0xEE, 0x2E, 0xF5, 0x26, 0x3D, 0x45, 0x59, 0x95, 0x9F, 0x4F, 0x9C, 0x42, 0x9F, |
| 0x9D, 0x12 } }; |
| const ble_uuid128_t UUID_CHIPoBLEChar_TX = { |
| { BLE_UUID_TYPE_128 }, { 0x12, 0x9D, 0x9F, 0x42, 0x9C, 0x4F, 0x9F, 0x95, 0x59, 0x45, 0x3D, 0x26, 0xF5, 0x2E, 0xEE, 0x18 } |
| }; |
| |
| #if CHIP_ENABLE_ADDITIONAL_DATA_ADVERTISING |
| const ble_uuid128_t UUID_CHIPoBLEChar_C3 = { |
| { BLE_UUID_TYPE_128 }, { 0x04, 0x8F, 0x21, 0x83, 0x8A, 0x74, 0x7D, 0xB8, 0xF2, 0x45, 0x72, 0x87, 0x38, 0x02, 0x63, 0x64 } |
| }; |
| #endif |
| |
| SemaphoreHandle_t semaphoreHandle = NULL; |
| |
| } // unnamed namespace |
| |
| BLEManagerImpl BLEManagerImpl::sInstance; |
| constexpr System::Clock::Timeout BLEManagerImpl::kAdvertiseTimeout; |
| constexpr System::Clock::Timeout BLEManagerImpl::kFastAdvertiseTimeout; |
| |
| const struct ble_gatt_svc_def BLEManagerImpl::CHIPoBLEGATTAttrs[] = { |
| { .type = BLE_GATT_SVC_TYPE_PRIMARY, |
| .uuid = (ble_uuid_t *) (&ShortUUID_CHIPoBLEService), |
| .characteristics = |
| (struct ble_gatt_chr_def[]){ |
| { |
| .uuid = (ble_uuid_t *) (&UUID128_CHIPoBLEChar_RX), |
| .access_cb = gatt_svr_chr_access, |
| .flags = BLE_GATT_CHR_F_WRITE, |
| .val_handle = &sInstance.mRXCharAttrHandle, |
| }, |
| { |
| .uuid = (ble_uuid_t *) (&UUID_CHIPoBLEChar_TX), |
| .access_cb = gatt_svr_chr_access, |
| .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY, |
| .val_handle = &sInstance.mTXCharCCCDAttrHandle, |
| }, |
| #if CHIP_ENABLE_ADDITIONAL_DATA_ADVERTISING |
| { |
| .uuid = (ble_uuid_t *) (&UUID_CHIPoBLEChar_C3), |
| .access_cb = gatt_svr_chr_access_additional_data, |
| .flags = BLE_GATT_CHR_F_READ, |
| .val_handle = &sInstance.mC3CharAttrHandle, |
| }, |
| #endif |
| { |
| 0, /* No more characteristics in this service */ |
| }, |
| } }, |
| |
| { |
| 0, /* No more services. */ |
| }, |
| }; |
| |
| CHIP_ERROR BLEManagerImpl::_Init() |
| { |
| CHIP_ERROR err; |
| |
| // Initialize the Chip BleLayer. |
| err = BleLayer::Init(this, this, &DeviceLayer::SystemLayer()); |
| SuccessOrExit(err); |
| |
| mRXCharAttrHandle = 0; |
| #if CHIP_ENABLE_ADDITIONAL_DATA_ADVERTISING |
| mC3CharAttrHandle = 0; |
| #endif |
| mTXCharCCCDAttrHandle = 0; |
| mFlags.ClearAll().Set(Flags::kAdvertisingEnabled, CHIP_DEVICE_CONFIG_CHIPOBLE_ENABLE_ADVERTISING_AUTOSTART); |
| mFlags.Set(Flags::kFastAdvertisingEnabled, true); |
| mNumGAPCons = 0; |
| memset(mCons, 0, sizeof(mCons)); |
| mServiceMode = ConnectivityManager::kCHIPoBLEServiceMode_Enabled; |
| memset(mDeviceName, 0, sizeof(mDeviceName)); |
| |
| PlatformMgr().ScheduleWork(DriveBLEState, 0); |
| |
| exit: |
| return err; |
| } |
| |
| CHIP_ERROR BLEManagerImpl::_SetCHIPoBLEServiceMode(CHIPoBLEServiceMode val) |
| { |
| 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) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| VerifyOrExit(mServiceMode != ConnectivityManager::kCHIPoBLEServiceMode_NotSupported, err = CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); |
| |
| if (val) |
| { |
| mAdvertiseStartTime = System::SystemClock().GetMonotonicTimestamp(); |
| ReturnErrorOnFailure(DeviceLayer::SystemLayer().StartTimer(kAdvertiseTimeout, HandleAdvertisementTimer, this)); |
| ReturnErrorOnFailure(DeviceLayer::SystemLayer().StartTimer(kFastAdvertiseTimeout, HandleFastAdvertisementTimer, this)); |
| } |
| |
| mFlags.Set(Flags::kFastAdvertisingEnabled, val); |
| mFlags.Set(Flags::kAdvertisingRefreshNeeded, 1); |
| mFlags.Set(Flags::kAdvertisingEnabled, val); |
| PlatformMgr().ScheduleWork(DriveBLEState, 0); |
| |
| exit: |
| return err; |
| } |
| |
| void BLEManagerImpl::HandleAdvertisementTimer(System::Layer * systemLayer, void * context) |
| { |
| static_cast<BLEManagerImpl *>(context)->HandleAdvertisementTimer(); |
| } |
| |
| void BLEManagerImpl::HandleAdvertisementTimer() |
| { |
| System::Clock::Timestamp currentTimestamp = System::SystemClock().GetMonotonicTimestamp(); |
| |
| if (currentTimestamp - mAdvertiseStartTime >= kAdvertiseTimeout) |
| { |
| mFlags.Set(Flags::kAdvertisingEnabled, 0); |
| PlatformMgr().ScheduleWork(DriveBLEState, 0); |
| } |
| } |
| |
| void BLEManagerImpl::HandleFastAdvertisementTimer(System::Layer * systemLayer, void * context) |
| { |
| static_cast<BLEManagerImpl *>(context)->HandleFastAdvertisementTimer(); |
| } |
| |
| void BLEManagerImpl::HandleFastAdvertisementTimer() |
| { |
| System::Clock::Timestamp currentTimestamp = System::SystemClock().GetMonotonicTimestamp(); |
| |
| if (currentTimestamp - mAdvertiseStartTime >= kFastAdvertiseTimeout) |
| { |
| mFlags.Set(Flags::kFastAdvertisingEnabled, 0); |
| mFlags.Set(Flags::kAdvertisingRefreshNeeded, 1); |
| PlatformMgr().ScheduleWork(DriveBLEState, 0); |
| } |
| } |
| |
| CHIP_ERROR BLEManagerImpl::_SetAdvertisingMode(BLEAdvertisingMode mode) |
| { |
| 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) |
| { |
| if (strlen(mDeviceName) >= bufSize) |
| { |
| return CHIP_ERROR_BUFFER_TOO_SMALL; |
| } |
| strcpy(buf, mDeviceName); |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR BLEManagerImpl::_SetDeviceName(const char * deviceName) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| VerifyOrExit(mServiceMode != ConnectivityManager::kCHIPoBLEServiceMode_NotSupported, err = CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); |
| if (deviceName != NULL && deviceName[0] != 0) |
| { |
| if (strlen(deviceName) >= kMaxDeviceNameLength) |
| { |
| return CHIP_ERROR_INVALID_ARGUMENT; |
| } |
| strcpy(mDeviceName, deviceName); |
| mFlags.Set(Flags::kUseCustomDeviceName); |
| } |
| else |
| { |
| mDeviceName[0] = 0; |
| mFlags.Clear(Flags::kUseCustomDeviceName); |
| } |
| |
| exit: |
| return err; |
| } |
| |
| void BLEManagerImpl::_OnPlatformEvent(const ChipDeviceEvent * event) |
| { |
| switch (event->Type) |
| { |
| case DeviceEventType::kCHIPoBLESubscribe: |
| HandleSubscribeReceived(event->CHIPoBLESubscribe.ConId, &CHIP_BLE_SVC_ID, &chipUUID_CHIPoBLEChar_TX); |
| { |
| ChipDeviceEvent connectionEvent; |
| connectionEvent.Type = DeviceEventType::kCHIPoBLEConnectionEstablished; |
| PlatformMgr().PostEventOrDie(&connectionEvent); |
| } |
| break; |
| |
| case DeviceEventType::kCHIPoBLEUnsubscribe: |
| HandleUnsubscribeReceived(event->CHIPoBLEUnsubscribe.ConId, &CHIP_BLE_SVC_ID, &chipUUID_CHIPoBLEChar_TX); |
| break; |
| |
| case DeviceEventType::kCHIPoBLEWriteReceived: |
| HandleWriteReceived(event->CHIPoBLEWriteReceived.ConId, &CHIP_BLE_SVC_ID, &chipUUID_CHIPoBLEChar_RX, |
| PacketBufferHandle::Adopt(event->CHIPoBLEWriteReceived.Data)); |
| break; |
| |
| case DeviceEventType::kCHIPoBLEIndicateConfirm: |
| HandleIndicationConfirmation(event->CHIPoBLEIndicateConfirm.ConId, &CHIP_BLE_SVC_ID, &chipUUID_CHIPoBLEChar_TX); |
| break; |
| |
| case DeviceEventType::kCHIPoBLEConnectionError: |
| HandleConnectionError(event->CHIPoBLEConnectionError.ConId, event->CHIPoBLEConnectionError.Reason); |
| break; |
| |
| case DeviceEventType::kFabricMembershipChange: |
| case DeviceEventType::kServiceProvisioningChange: |
| case DeviceEventType::kAccountPairingChange: |
| case DeviceEventType::kWiFiConnectivityChange: |
| |
| // If CHIPOBLE_DISABLE_ADVERTISING_WHEN_PROVISIONED is enabled, and there is a change to the |
| // device's provisioning state, then automatically disable CHIPoBLE advertising if the device |
| // is now fully provisioned. |
| #if CHIP_DEVICE_CONFIG_CHIPOBLE_DISABLE_ADVERTISING_WHEN_PROVISIONED |
| if (ConfigurationMgr().IsFullyProvisioned()) |
| { |
| mFlags.Clear(Flags::kAdvertisingEnabled); |
| ChipLogProgress(DeviceLayer, "CHIPoBLE advertising disabled because device is fully provisioned"); |
| } |
| #endif // CHIP_DEVICE_CONFIG_CHIPOBLE_DISABLE_ADVERTISING_WHEN_PROVISIONED |
| |
| // Force the advertising configuration to be refreshed to reflect new provisioning state. |
| ChipLogProgress(DeviceLayer, "Updating advertising data"); |
| mFlags.Clear(Flags::kAdvertisingConfigured); |
| mFlags.Set(Flags::kAdvertisingRefreshNeeded); |
| |
| DriveBLEState(); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| bool BLEManagerImpl::SubscribeCharacteristic(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId) |
| { |
| ChipLogProgress(DeviceLayer, "BLEManagerImpl::SubscribeCharacteristic() not supported"); |
| return false; |
| } |
| |
| bool BLEManagerImpl::UnsubscribeCharacteristic(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId) |
| { |
| ChipLogProgress(DeviceLayer, "BLEManagerImpl::UnsubscribeCharacteristic() not supported"); |
| return false; |
| } |
| |
| bool BLEManagerImpl::CloseConnection(BLE_CONNECTION_OBJECT conId) |
| { |
| CHIP_ERROR err; |
| |
| ChipLogProgress(DeviceLayer, "Closing BLE GATT connection (con %u)", conId); |
| |
| // Signal the ESP BLE layer to close the conneecction. |
| err = MapBLEError(ble_gap_terminate(conId, BLE_ERR_REM_USER_CONN_TERM)); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(DeviceLayer, "ble_gap_terminate() failed: %s", ErrorStr(err)); |
| } |
| |
| // Force a refresh of the advertising state. |
| mFlags.Set(Flags::kAdvertisingRefreshNeeded); |
| mFlags.Clear(Flags::kAdvertisingConfigured); |
| PlatformMgr().ScheduleWork(DriveBLEState, 0); |
| |
| return (err == CHIP_NO_ERROR); |
| } |
| |
| uint16_t BLEManagerImpl::GetMTU(BLE_CONNECTION_OBJECT conId) const |
| { |
| return ble_att_mtu(conId); |
| } |
| |
| bool BLEManagerImpl::SendIndication(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId, |
| PacketBufferHandle data) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| struct os_mbuf * om; |
| |
| VerifyOrExit(IsSubscribed(conId), err = CHIP_ERROR_INVALID_ARGUMENT); |
| |
| ESP_LOGD(TAG, "Sending indication for CHIPoBLE TX characteristic (con %u, len %u)", conId, data->DataLength()); |
| |
| om = ble_hs_mbuf_from_flat(data->Start(), data->DataLength()); |
| if (om == NULL) |
| { |
| ChipLogError(DeviceLayer, "ble_hs_mbuf_from_flat failed:"); |
| err = CHIP_ERROR_NO_MEMORY; |
| ExitNow(); |
| } |
| |
| err = MapBLEError(ble_gattc_notify_custom(conId, mTXCharCCCDAttrHandle, om)); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(DeviceLayer, "ble_gattc_notify_custom() failed: %s", ErrorStr(err)); |
| ExitNow(); |
| } |
| |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(DeviceLayer, "BLEManagerImpl::SendIndication() failed: %s", ErrorStr(err)); |
| return false; |
| } |
| return true; |
| } |
| |
| bool BLEManagerImpl::SendWriteRequest(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId, |
| PacketBufferHandle pBuf) |
| { |
| ChipLogError(DeviceLayer, "BLEManagerImpl::SendWriteRequest() not supported"); |
| return false; |
| } |
| |
| bool BLEManagerImpl::SendReadRequest(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId, |
| PacketBufferHandle pBuf) |
| { |
| ChipLogError(DeviceLayer, "BLEManagerImpl::SendReadRequest() not supported"); |
| return false; |
| } |
| |
| bool BLEManagerImpl::SendReadResponse(BLE_CONNECTION_OBJECT conId, BLE_READ_REQUEST_CONTEXT requestContext, |
| const ChipBleUUID * svcId, const ChipBleUUID * charId) |
| { |
| ChipLogError(DeviceLayer, "BLEManagerImpl::SendReadResponse() not supported"); |
| return false; |
| } |
| |
| void BLEManagerImpl::NotifyChipConnectionClosed(BLE_CONNECTION_OBJECT conId) {} |
| |
| CHIP_ERROR BLEManagerImpl::MapBLEError(int bleErr) |
| { |
| switch (bleErr) |
| { |
| case ESP_OK: |
| return CHIP_NO_ERROR; |
| case BLE_HS_EMSGSIZE: |
| return CHIP_ERROR_INVALID_MESSAGE_LENGTH; |
| case BLE_HS_ENOMEM: |
| case ESP_ERR_NO_MEM: |
| return CHIP_ERROR_NO_MEMORY; |
| case BLE_HS_ENOTCONN: |
| return CHIP_ERROR_NOT_CONNECTED; |
| case BLE_HS_ENOTSUP: |
| return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; |
| case BLE_HS_EAPP: |
| return CHIP_ERROR_READ_FAILED; |
| case BLE_HS_EBADDATA: |
| return CHIP_ERROR_DATA_NOT_ALIGNED; |
| case BLE_HS_ETIMEOUT: |
| return CHIP_ERROR_TIMEOUT; |
| case BLE_HS_ENOADDR: |
| return CHIP_ERROR_INVALID_ADDRESS; |
| case ESP_ERR_INVALID_ARG: |
| return CHIP_ERROR_INVALID_ARGUMENT; |
| default: |
| return CHIP_ERROR(ChipError::Range::kPlatform, CHIP_DEVICE_CONFIG_ESP32_BLE_ERROR_MIN + bleErr); |
| } |
| } |
| void BLEManagerImpl::DriveBLEState(void) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| // Perform any initialization actions that must occur after the Chip task is running. |
| if (!mFlags.Has(Flags::kAsyncInitCompleted)) |
| { |
| mFlags.Set(Flags::kAsyncInitCompleted); |
| |
| // If CHIP_DEVICE_CONFIG_CHIPOBLE_DISABLE_ADVERTISING_WHEN_PROVISIONED is enabled, |
| // disable CHIPoBLE advertising if the device is fully provisioned. |
| #if CHIP_DEVICE_CONFIG_CHIPOBLE_DISABLE_ADVERTISING_WHEN_PROVISIONED |
| if (ConfigurationMgr().IsFullyProvisioned()) |
| { |
| mFlags.Clear(Flags::kAdvertisingEnabled); |
| ChipLogProgress(DeviceLayer, "CHIPoBLE advertising disabled because device is fully provisioned"); |
| } |
| #endif // CHIP_DEVICE_CONFIG_CHIPOBLE_DISABLE_ADVERTISING_WHEN_PROVISIONED |
| } |
| |
| // Initializes the ESP BLE layer if needed. |
| if (mServiceMode == ConnectivityManager::kCHIPoBLEServiceMode_Enabled && !mFlags.Has(Flags::kESPBLELayerInitialized)) |
| { |
| err = InitESPBleLayer(); |
| SuccessOrExit(err); |
| |
| // Add delay of 500msec while NimBLE host task gets up and running |
| { |
| vTaskDelay(500 / portTICK_RATE_MS); |
| } |
| } |
| |
| // If the application has enabled CHIPoBLE and BLE advertising... |
| if (mServiceMode == ConnectivityManager::kCHIPoBLEServiceMode_Enabled && |
| mFlags.Has(Flags::kAdvertisingEnabled) |
| #if CHIP_DEVICE_CONFIG_CHIPOBLE_SINGLE_CONNECTION |
| // and no connections are active... |
| && (_NumConnections() == 0) |
| #endif |
| ) |
| { |
| // Start/re-start advertising if not already advertising, or if the advertising state of the |
| // ESP BLE layer needs to be refreshed. |
| if (!mFlags.Has(Flags::kAdvertising) || mFlags.Has(Flags::kAdvertisingRefreshNeeded)) |
| { |
| // Configure advertising data if it hasn't been done yet. This is an asynchronous step which |
| // must complete before advertising can be started. When that happens, this method will |
| // be called again, and execution will proceed to the code below. |
| if (!mFlags.Has(Flags::kAdvertisingConfigured)) |
| { |
| err = ConfigureAdvertisingData(); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(DeviceLayer, "Configure Adv Data failed: %s", ErrorStr(err)); |
| ExitNow(); |
| } |
| } |
| |
| // Start advertising. This is also an asynchronous step. |
| ESP_LOGD(TAG, "NimBLE start advertising..."); |
| err = StartAdvertising(); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(DeviceLayer, "Start advertising failed: %s", ErrorStr(err)); |
| ExitNow(); |
| } |
| |
| mFlags.Clear(Flags::kAdvertisingRefreshNeeded); |
| // Transition to the Advertising state... |
| if (!mFlags.Has(Flags::kAdvertising)) |
| { |
| ChipLogProgress(DeviceLayer, "CHIPoBLE advertising started"); |
| |
| mFlags.Set(Flags::kAdvertising); |
| |
| // Post a CHIPoBLEAdvertisingChange(Started) event. |
| { |
| ChipDeviceEvent advChange; |
| advChange.Type = DeviceEventType::kCHIPoBLEAdvertisingChange; |
| advChange.CHIPoBLEAdvertisingChange.Result = kActivity_Started; |
| err = PlatformMgr().PostEvent(&advChange); |
| } |
| } |
| } |
| } |
| |
| // Otherwise stop advertising if needed... |
| else |
| { |
| if (mFlags.Has(Flags::kAdvertising)) |
| { |
| if (ble_gap_adv_active()) |
| { |
| err = MapBLEError(ble_gap_adv_stop()); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(DeviceLayer, "ble_gap_adv_stop() failed: %s", ErrorStr(err)); |
| ExitNow(); |
| } |
| } |
| // mFlags.Clear(Flags::kAdvertisingRefreshNeeded); |
| |
| // Transition to the not Advertising state... |
| if (mFlags.Has(Flags::kAdvertising)) |
| { |
| mFlags.Clear(Flags::kAdvertising); |
| mFlags.Set(Flags::kFastAdvertisingEnabled, true); |
| |
| ChipLogProgress(DeviceLayer, "CHIPoBLE advertising stopped"); |
| |
| // Post a CHIPoBLEAdvertisingChange(Stopped) event. |
| { |
| ChipDeviceEvent advChange; |
| advChange.Type = DeviceEventType::kCHIPoBLEAdvertisingChange; |
| advChange.CHIPoBLEAdvertisingChange.Result = kActivity_Stopped; |
| err = PlatformMgr().PostEvent(&advChange); |
| } |
| } |
| |
| ExitNow(); |
| } |
| } |
| |
| // Stop the CHIPoBLE GATT service if needed. |
| if (mServiceMode != ConnectivityManager::kCHIPoBLEServiceMode_Enabled && mFlags.Has(Flags::kGATTServiceStarted)) |
| { |
| // TODO: Not supported |
| } |
| |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(DeviceLayer, "Disabling CHIPoBLE service due to error: %s", ErrorStr(err)); |
| mServiceMode = ConnectivityManager::kCHIPoBLEServiceMode_Disabled; |
| } |
| } |
| |
| void BLEManagerImpl::bleprph_on_reset(int reason) |
| { |
| ESP_LOGE(TAG, "Resetting state; reason=%d\n", reason); |
| } |
| |
| void BLEManagerImpl::bleprph_on_sync(void) |
| { |
| ESP_LOGI(TAG, "BLE host-controller synced"); |
| xSemaphoreGive(semaphoreHandle); |
| } |
| |
| void BLEManagerImpl::bleprph_host_task(void * param) |
| { |
| ESP_LOGD(TAG, "BLE Host Task Started"); |
| /* This function will return only when nimble_port_stop() is executed */ |
| nimble_port_run(); |
| nimble_port_freertos_deinit(); |
| } |
| |
| CHIP_ERROR BLEManagerImpl::InitESPBleLayer(void) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| VerifyOrExit(!mFlags.Has(Flags::kESPBLELayerInitialized), /* */); |
| |
| semaphoreHandle = xSemaphoreCreateBinary(); |
| if (semaphoreHandle == NULL) |
| { |
| err = CHIP_ERROR_NO_MEMORY; |
| ESP_LOGE(TAG, "Failed to create semaphore"); |
| ExitNow(); |
| } |
| |
| for (int i = 0; i < kMaxConnections; i++) |
| { |
| mSubscribedConIds[i] = BLE_CONNECTION_UNINITIALIZED; |
| } |
| |
| err = MapBLEError(esp_nimble_hci_and_controller_init()); |
| SuccessOrExit(err); |
| |
| nimble_port_init(); |
| |
| /* Initialize the NimBLE host configuration. */ |
| ble_hs_cfg.reset_cb = bleprph_on_reset; |
| ble_hs_cfg.sync_cb = bleprph_on_sync; |
| ble_hs_cfg.store_status_cb = ble_store_util_status_rr; |
| ble_hs_cfg.sm_bonding = 1; |
| ble_hs_cfg.sm_our_key_dist = BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID; |
| ble_hs_cfg.sm_their_key_dist = BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID; |
| |
| // Register the CHIPoBLE GATT attributes with the ESP BLE layer if needed. |
| if (mServiceMode == ConnectivityManager::kCHIPoBLEServiceMode_Enabled) |
| { |
| ble_svc_gap_init(); |
| ble_svc_gatt_init(); |
| |
| err = MapBLEError(ble_gatts_count_cfg(CHIPoBLEGATTAttrs)); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(DeviceLayer, "ble_gatts_count_cfg failed: %s", ErrorStr(err)); |
| ExitNow(); |
| } |
| |
| err = MapBLEError(ble_gatts_add_svcs(CHIPoBLEGATTAttrs)); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(DeviceLayer, "ble_gatts_add_svcs failed: %s", ErrorStr(err)); |
| ExitNow(); |
| } |
| } |
| |
| nimble_port_freertos_init(bleprph_host_task); |
| |
| xSemaphoreTake(semaphoreHandle, portMAX_DELAY); |
| vSemaphoreDelete(semaphoreHandle); |
| semaphoreHandle = NULL; |
| |
| sInstance.mFlags.Set(Flags::kESPBLELayerInitialized); |
| sInstance.mFlags.Set(Flags::kGATTServiceStarted); |
| |
| exit: |
| return err; |
| } |
| |
| CHIP_ERROR BLEManagerImpl::ConfigureAdvertisingData(void) |
| { |
| CHIP_ERROR err; |
| uint8_t advData[MAX_ADV_DATA_LEN]; |
| uint8_t index = 0; |
| |
| constexpr uint8_t kServiceDataTypeSize = 1; |
| |
| chip::Ble::ChipBLEDeviceIdentificationInfo deviceIdInfo; |
| |
| // If a custom device name has not been specified, generate a CHIP-standard name based on the |
| // bottom digits of the Chip device id. |
| uint16_t discriminator; |
| SuccessOrExit(err = ConfigurationMgr().GetSetupDiscriminator(discriminator)); |
| |
| if (!mFlags.Has(Flags::kUseCustomDeviceName)) |
| { |
| snprintf(mDeviceName, sizeof(mDeviceName), "%s%04u", CHIP_DEVICE_CONFIG_BLE_DEVICE_NAME_PREFIX, discriminator); |
| mDeviceName[kMaxDeviceNameLength] = 0; |
| } |
| |
| // Configure the BLE device name. |
| err = MapBLEError(ble_svc_gap_device_name_set(mDeviceName)); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(DeviceLayer, "ble_svc_gap_device_name_set() failed: %s", ErrorStr(err)); |
| ExitNow(); |
| } |
| |
| memset(advData, 0, sizeof(advData)); |
| advData[index++] = 0x02; // length |
| advData[index++] = CHIP_ADV_DATA_TYPE_FLAGS; // AD type : flags |
| advData[index++] = CHIP_ADV_DATA_FLAGS; // AD value |
| advData[index++] = kServiceDataTypeSize + sizeof(ESP32ChipServiceData); // length |
| advData[index++] = CHIP_ADV_DATA_TYPE_SERVICE_DATA; // AD type: (Service Data - 16-bit UUID) |
| advData[index++] = static_cast<uint8_t>(ShortUUID_CHIPoBLEService.value & 0xFF); // AD value |
| advData[index++] = static_cast<uint8_t>((ShortUUID_CHIPoBLEService.value >> 8) & 0xFF); // AD value |
| |
| err = ConfigurationMgr().GetBLEDeviceIdentificationInfo(deviceIdInfo); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(DeviceLayer, "GetBLEDeviceIdentificationInfo(): %s", ErrorStr(err)); |
| ExitNow(); |
| } |
| |
| #if CHIP_ENABLE_ADDITIONAL_DATA_ADVERTISING |
| deviceIdInfo.SetAdditionalDataFlag(true); |
| #endif |
| |
| VerifyOrExit(index + sizeof(deviceIdInfo) <= sizeof(advData), err = CHIP_ERROR_OUTBOUND_MESSAGE_TOO_BIG); |
| memcpy(&advData[index], &deviceIdInfo, sizeof(deviceIdInfo)); |
| index = static_cast<uint8_t>(index + sizeof(deviceIdInfo)); |
| |
| // Construct the Chip BLE Service Data to be sent in the scan response packet. |
| err = MapBLEError(ble_gap_adv_set_data(advData, sizeof(advData))); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(DeviceLayer, "ble_gap_adv_set_data failed: %s %d", ErrorStr(err), discriminator); |
| ExitNow(); |
| } |
| |
| exit: |
| return err; |
| } |
| |
| void BLEManagerImpl::HandleRXCharWrite(struct ble_gatt_char_context * param) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| uint16_t data_len = 0; |
| |
| ESP_LOGI(TAG, "Write request received for CHIPoBLE RX characteristic con %u %u", param->conn_handle, param->attr_handle); |
| |
| // Copy the data to a packet buffer. |
| data_len = OS_MBUF_PKTLEN(param->ctxt->om); |
| PacketBufferHandle buf = System::PacketBufferHandle::New(data_len, 0); |
| VerifyOrExit(!buf.IsNull(), err = CHIP_ERROR_NO_MEMORY); |
| VerifyOrExit(buf->AvailableDataLength() >= data_len, err = CHIP_ERROR_BUFFER_TOO_SMALL); |
| ble_hs_mbuf_to_flat(param->ctxt->om, buf->Start(), data_len, NULL); |
| buf->SetDataLength(data_len); |
| |
| // Post an event to the Chip queue to deliver the data into the Chip stack. |
| { |
| ChipDeviceEvent event; |
| event.Type = DeviceEventType::kCHIPoBLEWriteReceived; |
| event.CHIPoBLEWriteReceived.ConId = param->conn_handle; |
| event.CHIPoBLEWriteReceived.Data = std::move(buf).UnsafeRelease(); |
| err = PlatformMgr().PostEvent(&event); |
| } |
| |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(DeviceLayer, "HandleRXCharWrite() failed: %s", ErrorStr(err)); |
| } |
| } |
| |
| void BLEManagerImpl::HandleTXCharRead(struct ble_gatt_char_context * param) |
| { |
| /* Not supported */ |
| ChipLogError(DeviceLayer, "BLEManagerImpl::HandleTXCharRead() not supported"); |
| } |
| |
| void BLEManagerImpl::HandleTXCharCCCDRead(void * param) |
| { |
| /* Not Supported */ |
| ChipLogError(DeviceLayer, "BLEManagerImpl::HandleTXCharCCCDRead() not supported"); |
| } |
| |
| void BLEManagerImpl::HandleTXCharCCCDWrite(struct ble_gap_event * gapEvent) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| bool indicationsEnabled; |
| bool notificationsEnabled; |
| |
| ChipLogProgress(DeviceLayer, |
| "Write request/command received for CHIPoBLE TX CCCD characteristic (con %" PRIu16 |
| " ) indicate = %d notify = %d", |
| gapEvent->subscribe.conn_handle, gapEvent->subscribe.cur_indicate, gapEvent->subscribe.cur_notify); |
| |
| // Determine if the client is enabling or disabling indications/notification. |
| indicationsEnabled = gapEvent->subscribe.cur_indicate; |
| notificationsEnabled = gapEvent->subscribe.cur_notify; |
| |
| // If the client has requested to enabled indications/notifications |
| if (indicationsEnabled || notificationsEnabled) |
| { |
| // If indications are not already enabled for the connection... |
| if (!IsSubscribed(gapEvent->subscribe.conn_handle)) |
| { |
| // Record that indications have been enabled for this connection. If this fails because |
| err = SetSubscribed(gapEvent->subscribe.conn_handle); |
| VerifyOrExit(err != CHIP_ERROR_NO_MEMORY, err = CHIP_NO_ERROR); |
| SuccessOrExit(err); |
| } |
| } |
| |
| else |
| { |
| // If indications had previously been enabled for this connection, record that they are no longer |
| // enabled. |
| UnsetSubscribed(gapEvent->subscribe.conn_handle); |
| } |
| |
| // Post an event to the Chip queue to process either a CHIPoBLE Subscribe or Unsubscribe based on |
| // whether the client is enabling or disabling indications. |
| { |
| ChipDeviceEvent event; |
| event.Type = (indicationsEnabled || notificationsEnabled) ? DeviceEventType::kCHIPoBLESubscribe |
| : DeviceEventType::kCHIPoBLEUnsubscribe; |
| event.CHIPoBLESubscribe.ConId = gapEvent->subscribe.conn_handle; |
| err = PlatformMgr().PostEvent(&event); |
| } |
| |
| ChipLogProgress(DeviceLayer, "CHIPoBLE %s received", |
| (indicationsEnabled || notificationsEnabled) ? "subscribe" : "unsubscribe"); |
| |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(DeviceLayer, "HandleTXCharCCCDWrite() failed: %s", ErrorStr(err)); |
| // TODO: fail connection??? |
| } |
| |
| return; |
| } |
| |
| CHIP_ERROR BLEManagerImpl::HandleTXComplete(struct ble_gap_event * gapEvent) |
| { |
| ChipLogProgress(DeviceLayer, "Confirm received for CHIPoBLE TX characteristic indication (con %" PRIu16 ") status= %d ", |
| gapEvent->notify_tx.conn_handle, gapEvent->notify_tx.status); |
| |
| // Signal the BLE Layer that the outstanding indication is complete. |
| if (gapEvent->notify_tx.status == 0 || gapEvent->notify_tx.status == BLE_HS_EDONE) |
| { |
| // Post an event to the Chip queue to process the indicate confirmation. |
| ChipDeviceEvent event; |
| event.Type = DeviceEventType::kCHIPoBLEIndicateConfirm; |
| event.CHIPoBLEIndicateConfirm.ConId = gapEvent->notify_tx.conn_handle; |
| ReturnErrorOnFailure(PlatformMgr().PostEvent(&event)); |
| } |
| |
| else |
| { |
| ChipDeviceEvent event; |
| event.Type = DeviceEventType::kCHIPoBLEConnectionError; |
| event.CHIPoBLEConnectionError.ConId = gapEvent->notify_tx.conn_handle; |
| event.CHIPoBLEConnectionError.Reason = BLE_ERROR_CHIPOBLE_PROTOCOL_ABORT; |
| ReturnErrorOnFailure(PlatformMgr().PostEvent(&event)); |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| uint16_t BLEManagerImpl::_NumConnections(void) |
| { |
| uint16_t numCons = 0; |
| for (uint16_t i = 0; i < kMaxConnections; i++) |
| { |
| if (mSubscribedConIds[i] != BLE_CONNECTION_UNINITIALIZED) |
| { |
| numCons++; |
| } |
| } |
| |
| return numCons; |
| } |
| |
| CHIP_ERROR BLEManagerImpl::HandleGAPConnect(struct ble_gap_event * gapEvent) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| ChipLogProgress(DeviceLayer, "BLE GAP connection established (con %" PRIu16 ")", gapEvent->connect.conn_handle); |
| |
| // Track the number of active GAP connections. |
| mNumGAPCons++; |
| err = SetSubscribed(gapEvent->connect.conn_handle); |
| VerifyOrExit(err != CHIP_ERROR_NO_MEMORY, err = CHIP_NO_ERROR); |
| SuccessOrExit(err); |
| |
| mFlags.Set(Flags::kAdvertisingRefreshNeeded); |
| mFlags.Clear(Flags::kAdvertisingConfigured); |
| |
| exit: |
| return err; |
| } |
| |
| CHIP_ERROR BLEManagerImpl::HandleGAPDisconnect(struct ble_gap_event * gapEvent) |
| { |
| ChipLogProgress(DeviceLayer, "BLE GAP connection terminated (con %" PRIu16 " reason 0x%02" PRIx8 ")", |
| gapEvent->disconnect.conn.conn_handle, gapEvent->disconnect.reason); |
| |
| // Update the number of GAP connections. |
| if (mNumGAPCons > 0) |
| { |
| mNumGAPCons--; |
| } |
| |
| if (UnsetSubscribed(gapEvent->disconnect.conn.conn_handle)) |
| { |
| CHIP_ERROR disconReason; |
| switch (gapEvent->disconnect.reason) |
| { |
| case BLE_ERR_REM_USER_CONN_TERM: |
| disconReason = BLE_ERROR_REMOTE_DEVICE_DISCONNECTED; |
| break; |
| case BLE_ERR_CONN_TERM_LOCAL: |
| disconReason = BLE_ERROR_APP_CLOSED_CONNECTION; |
| break; |
| default: |
| disconReason = BLE_ERROR_CHIPOBLE_PROTOCOL_ABORT; |
| break; |
| } |
| HandleConnectionError(gapEvent->disconnect.conn.conn_handle, disconReason); |
| } |
| |
| ChipDeviceEvent disconnectEvent; |
| disconnectEvent.Type = DeviceEventType::kCHIPoBLEConnectionClosed; |
| ReturnErrorOnFailure(PlatformMgr().PostEvent(&disconnectEvent)); |
| |
| // Force a reconfiguration of advertising in case we switched to non-connectable mode when |
| // the BLE connection was established. |
| mFlags.Set(Flags::kAdvertisingRefreshNeeded); |
| mFlags.Clear(Flags::kAdvertisingConfigured); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR BLEManagerImpl::SetSubscribed(uint16_t conId) |
| { |
| uint16_t freeIndex = kMaxConnections; |
| |
| for (uint16_t i = 0; i < kMaxConnections; i++) |
| { |
| if (mSubscribedConIds[i] == conId) |
| { |
| return CHIP_NO_ERROR; |
| } |
| else if (mSubscribedConIds[i] == BLE_CONNECTION_UNINITIALIZED && i < freeIndex) |
| { |
| freeIndex = i; |
| } |
| } |
| |
| if (freeIndex < kMaxConnections) |
| { |
| mSubscribedConIds[freeIndex] = conId; |
| return CHIP_NO_ERROR; |
| } |
| else |
| { |
| return CHIP_ERROR_NO_MEMORY; |
| } |
| } |
| |
| bool BLEManagerImpl::UnsetSubscribed(uint16_t conId) |
| { |
| for (uint16_t i = 0; i < kMaxConnections; i++) |
| { |
| if (mSubscribedConIds[i] == conId) |
| { |
| mSubscribedConIds[i] = BLE_CONNECTION_UNINITIALIZED; |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool BLEManagerImpl::IsSubscribed(uint16_t conId) |
| { |
| if (conId != BLE_CONNECTION_UNINITIALIZED) |
| { |
| for (uint16_t i = 0; i < kMaxConnections; i++) |
| { |
| if (mSubscribedConIds[i] == conId) |
| { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| int BLEManagerImpl::ble_svr_gap_event(struct ble_gap_event * event, void * arg) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| switch (event->type) |
| { |
| case BLE_GAP_EVENT_CONNECT: |
| /* A new connection was established or a connection attempt failed */ |
| err = sInstance.HandleGAPConnect(event); |
| SuccessOrExit(err); |
| break; |
| |
| case BLE_GAP_EVENT_DISCONNECT: |
| err = sInstance.HandleGAPDisconnect(event); |
| SuccessOrExit(err); |
| break; |
| |
| case BLE_GAP_EVENT_ADV_COMPLETE: |
| ESP_LOGD(TAG, "BLE_GAP_EVENT_ADV_COMPLETE event"); |
| break; |
| |
| case BLE_GAP_EVENT_SUBSCRIBE: |
| if (event->subscribe.attr_handle == sInstance.mTXCharCCCDAttrHandle) |
| { |
| sInstance.HandleTXCharCCCDWrite(event); |
| } |
| |
| break; |
| |
| case BLE_GAP_EVENT_NOTIFY_TX: |
| err = sInstance.HandleTXComplete(event); |
| SuccessOrExit(err); |
| break; |
| |
| case BLE_GAP_EVENT_MTU: |
| ESP_LOGD(TAG, "BLE_GAP_EVENT_MTU = %d channel id = %d", event->mtu.value, event->mtu.channel_id); |
| break; |
| |
| default: |
| break; |
| } |
| |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(DeviceLayer, "Disabling CHIPoBLE service due to error: %s", ErrorStr(err)); |
| sInstance.mServiceMode = ConnectivityManager::kCHIPoBLEServiceMode_Disabled; |
| } |
| |
| // Schedule DriveBLEState() to run. |
| PlatformMgr().ScheduleWork(DriveBLEState, 0); |
| |
| return err.AsInteger(); |
| } |
| |
| #if CHIP_ENABLE_ADDITIONAL_DATA_ADVERTISING |
| void BLEManagerImpl::HandleC3CharRead(struct ble_gatt_char_context * param) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| chip::System::PacketBufferHandle bufferHandle; |
| |
| char serialNumber[ConfigurationManager::kMaxSerialNumberLength + 1]; |
| uint16_t lifetimeCounter = 0; |
| BitFlags<AdditionalDataFields> additionalDataFields; |
| |
| #if CHIP_ENABLE_ROTATING_DEVICE_ID |
| err = ConfigurationMgr().GetSerialNumber(serialNumber, sizeof(serialNumber)); |
| SuccessOrExit(err); |
| err = ConfigurationMgr().GetLifetimeCounter(lifetimeCounter); |
| SuccessOrExit(err); |
| |
| additionalDataFields.Set(AdditionalDataFields::RotatingDeviceId); |
| #endif /* CHIP_ENABLE_ROTATING_DEVICE_ID */ |
| |
| err = AdditionalDataPayloadGenerator().generateAdditionalDataPayload(lifetimeCounter, serialNumber, strlen(serialNumber), |
| bufferHandle, additionalDataFields); |
| SuccessOrExit(err); |
| |
| os_mbuf_append(param->ctxt->om, bufferHandle->Start(), bufferHandle->DataLength()); |
| |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(DeviceLayer, "Failed to generate TLV encoded Additional Data (%s)", __func__); |
| } |
| return; |
| } |
| |
| int BLEManagerImpl::gatt_svr_chr_access_additional_data(uint16_t conn_handle, uint16_t attr_handle, |
| struct ble_gatt_access_ctxt * ctxt, void * arg) |
| { |
| struct ble_gatt_char_context param; |
| int err = 0; |
| |
| memset(¶m, 0, sizeof(struct ble_gatt_char_context)); |
| |
| switch (ctxt->op) |
| { |
| case BLE_GATT_ACCESS_OP_READ_CHR: |
| |
| param.conn_handle = conn_handle; |
| param.attr_handle = attr_handle; |
| param.ctxt = ctxt; |
| param.arg = arg; |
| sInstance.HandleC3CharRead(¶m); |
| break; |
| |
| default: |
| err = BLE_ATT_ERR_UNLIKELY; |
| break; |
| } |
| |
| PlatformMgr().ScheduleWork(DriveBLEState, 0); |
| |
| return err; |
| } |
| #endif /* CHIP_ENABLE_ADDITIONAL_DATA_ADVERTISING */ |
| |
| int BLEManagerImpl::gatt_svr_chr_access(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt * ctxt, void * arg) |
| { |
| struct ble_gatt_char_context param; |
| int err = 0; |
| |
| memset(¶m, 0, sizeof(struct ble_gatt_char_context)); |
| |
| switch (ctxt->op) |
| { |
| case BLE_GATT_ACCESS_OP_READ_CHR: |
| |
| param.conn_handle = conn_handle; |
| param.attr_handle = attr_handle; |
| param.ctxt = ctxt; |
| param.arg = arg; |
| sInstance.HandleTXCharRead(¶m); |
| break; |
| |
| case BLE_GATT_ACCESS_OP_READ_DSC: |
| |
| param.conn_handle = conn_handle; |
| param.attr_handle = attr_handle; |
| param.ctxt = ctxt; |
| param.arg = arg; |
| sInstance.HandleTXCharCCCDRead(¶m); |
| break; |
| |
| case BLE_GATT_ACCESS_OP_WRITE_CHR: |
| param.conn_handle = conn_handle; |
| param.attr_handle = attr_handle; |
| param.ctxt = ctxt; |
| param.arg = arg; |
| sInstance.HandleRXCharWrite(¶m); |
| break; |
| |
| default: |
| err = BLE_ATT_ERR_UNLIKELY; |
| break; |
| } |
| |
| PlatformMgr().ScheduleWork(DriveBLEState, 0); |
| |
| return err; |
| } |
| |
| CHIP_ERROR BLEManagerImpl::StartAdvertising(void) |
| { |
| CHIP_ERROR err; |
| ble_gap_adv_params adv_params; |
| memset(&adv_params, 0, sizeof(adv_params)); |
| #ifdef CONFIG_BT_NIMBLE_HOST_BASED_PRIVACY |
| uint8_t own_addr_type = BLE_OWN_ADDR_RANDOM; |
| #else |
| uint8_t own_addr_type = BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT; |
| #endif |
| adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; |
| |
| mFlags.Clear(Flags::kAdvertisingRefreshNeeded); |
| |
| // Advertise connectable if we haven't reached the maximum number of connections. |
| size_t numCons = _NumConnections(); |
| bool connectable = (numCons < kMaxConnections); |
| adv_params.conn_mode = connectable ? BLE_GAP_CONN_MODE_UND : BLE_GAP_CONN_MODE_NON; |
| |
| // Advertise in fast mode if it is connectable advertisement and |
| // the application has expressly requested fast advertising. |
| if (connectable && mFlags.Has(Flags::kFastAdvertisingEnabled)) |
| { |
| adv_params.itvl_min = CHIP_DEVICE_CONFIG_BLE_FAST_ADVERTISING_INTERVAL_MIN; |
| adv_params.itvl_max = CHIP_DEVICE_CONFIG_BLE_FAST_ADVERTISING_INTERVAL_MAX; |
| } |
| else |
| { |
| adv_params.itvl_min = CHIP_DEVICE_CONFIG_BLE_SLOW_ADVERTISING_INTERVAL_MIN; |
| adv_params.itvl_max = CHIP_DEVICE_CONFIG_BLE_SLOW_ADVERTISING_INTERVAL_MAX; |
| } |
| |
| ChipLogProgress(DeviceLayer, "Configuring CHIPoBLE advertising (interval %" PRIu32 " ms, %sconnectable, device name %s)", |
| (((uint32_t) adv_params.itvl_min) * 10) / 16, (connectable) ? "" : "non-", mDeviceName); |
| |
| { |
| if (ble_gap_adv_active()) |
| { |
| /* Advertising is already active. Stop and restart with the new parameters */ |
| ChipLogProgress(DeviceLayer, "Device already advertising, stop active advertisement and restart"); |
| err = MapBLEError(ble_gap_adv_stop()); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(DeviceLayer, "ble_gap_adv_stop() failed: %s, cannot restart", ErrorStr(err)); |
| return err; |
| } |
| } |
| #if CONFIG_BT_NIMBLE_HOST_BASED_PRIVACY |
| else |
| { |
| err = MapBLEError(ble_hs_pvcy_rpa_config(NIMBLE_HOST_ENABLE_RPA)); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(DeviceLayer, "RPA not set: %s", ErrorStr(err)); |
| return err; |
| } |
| } |
| #endif |
| err = MapBLEError(ble_gap_adv_start(own_addr_type, NULL, BLE_HS_FOREVER, &adv_params, ble_svr_gap_event, NULL)); |
| if (err == CHIP_NO_ERROR) |
| { |
| return CHIP_NO_ERROR; |
| } |
| else |
| { |
| ChipLogError(DeviceLayer, "ble_gap_adv_start() failed: %s", ErrorStr(err)); |
| return err; |
| } |
| } |
| } |
| |
| void BLEManagerImpl::DriveBLEState(intptr_t arg) |
| { |
| sInstance.DriveBLEState(); |
| } |
| |
| } // namespace Internal |
| } // namespace DeviceLayer |
| } // namespace chip |
| |
| #endif // CONFIG_BT_NIMBLE_ENABLED |
| |
| #endif // CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE |