| /* |
| * |
| * 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. |
| */ |
| |
| #include "DFUOverSMP.h" |
| |
| #if !defined(CONFIG_MCUMGR_SMP_BT) || !defined(CONFIG_MCUMGR_CMD_IMG_MGMT) || !defined(CONFIG_MCUMGR_CMD_OS_MGMT) |
| #error "DFUOverSMP requires MCUMGR module configs enabled" |
| #endif |
| |
| #include <img_mgmt/img_mgmt.h> |
| #include <os_mgmt/os_mgmt.h> |
| #include <zephyr/dfu/mcuboot.h> |
| #include <zephyr/mgmt/mcumgr/smp_bt.h> |
| |
| #include <platform/CHIPDeviceLayer.h> |
| |
| #include <lib/support/logging/CHIPLogging.h> |
| |
| #include "OTAUtil.h" |
| |
| using namespace ::chip::DeviceLayer; |
| |
| constexpr uint16_t kAdvertisingIntervalMinMs = 400; |
| constexpr uint16_t kAdvertisingIntervalMaxMs = 500; |
| |
| DFUOverSMP DFUOverSMP::sDFUOverSMP; |
| |
| void DFUOverSMP::Init(DFUOverSMPRestartAdvertisingHandler startAdvertisingCb) |
| { |
| os_mgmt_register_group(); |
| img_mgmt_register_group(); |
| img_mgmt_set_upload_cb(UploadConfirmHandler); |
| |
| memset(&mBleConnCallbacks, 0, sizeof(mBleConnCallbacks)); |
| mBleConnCallbacks.connected = nullptr; |
| mBleConnCallbacks.disconnected = OnBleDisconnect; |
| |
| bt_conn_cb_register(&mBleConnCallbacks); |
| |
| mgmt_register_evt_cb([](uint8_t opcode, uint16_t group, uint8_t id, void * arg) { |
| switch (opcode) |
| { |
| case MGMT_EVT_OP_CMD_RECV: |
| GetFlashHandler().DoAction(ExternalFlashManager::Action::WAKE_UP); |
| break; |
| case MGMT_EVT_OP_CMD_DONE: |
| GetFlashHandler().DoAction(ExternalFlashManager::Action::SLEEP); |
| break; |
| default: |
| break; |
| } |
| }); |
| |
| restartAdvertisingCallback = startAdvertisingCb; |
| |
| PlatformMgr().AddEventHandler(ChipEventHandler, 0); |
| } |
| |
| void DFUOverSMP::ConfirmNewImage() |
| { |
| // Check if the image is run in the REVERT mode and eventually |
| // confirm it to prevent reverting on the next boot. |
| if (mcuboot_swap_type() == BOOT_SWAP_TYPE_REVERT) |
| { |
| if (boot_write_img_confirmed()) |
| { |
| ChipLogError(DeviceLayer, "Confirming firmware image failed, it will be reverted on the next boot."); |
| } |
| else |
| { |
| ChipLogProgress(DeviceLayer, "New firmware image confirmed."); |
| } |
| } |
| } |
| |
| int DFUOverSMP::UploadConfirmHandler(const struct img_mgmt_upload_req req, const struct img_mgmt_upload_action action) |
| { |
| // For now just print update progress and confirm data chunk without any additional checks. |
| ChipLogProgress(DeviceLayer, "Software update progress of image %u: %u B / %u B", static_cast<unsigned>(req.image), |
| static_cast<unsigned>(req.off), static_cast<unsigned>(action.size)); |
| |
| return 0; |
| } |
| |
| void DFUOverSMP::StartServer() |
| { |
| if (!mIsEnabled) |
| { |
| mIsEnabled = true; |
| smp_bt_register(); |
| |
| ChipLogProgress(DeviceLayer, "Enabled software update"); |
| |
| // Start SMP advertising only in case CHIPoBLE advertising is not working. |
| if (!ConnectivityMgr().IsBLEAdvertisingEnabled()) |
| StartBLEAdvertising(); |
| } |
| else |
| { |
| ChipLogProgress(DeviceLayer, "Software update is already enabled"); |
| } |
| } |
| |
| void DFUOverSMP::StartBLEAdvertising() |
| { |
| if (!mIsEnabled && !mIsAdvertisingEnabled) |
| return; |
| |
| const char * deviceName = bt_get_name(); |
| const uint8_t advFlags = BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR; |
| |
| bt_data ad[] = { BT_DATA(BT_DATA_FLAGS, &advFlags, sizeof(advFlags)), |
| BT_DATA(BT_DATA_NAME_COMPLETE, deviceName, static_cast<uint8_t>(strlen(deviceName))) }; |
| |
| int rc; |
| bt_le_adv_param advParams = BT_LE_ADV_PARAM_INIT(BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_ONE_TIME, kAdvertisingIntervalMinMs, |
| kAdvertisingIntervalMaxMs, nullptr); |
| |
| rc = bt_le_adv_stop(); |
| if (rc) |
| { |
| ChipLogError(DeviceLayer, "SMP advertising stop failed (rc %d)", rc); |
| } |
| |
| rc = bt_le_adv_start(&advParams, ad, ARRAY_SIZE(ad), NULL, 0); |
| if (rc) |
| { |
| ChipLogError(DeviceLayer, "SMP advertising start failed (rc %d)", rc); |
| } |
| else |
| { |
| ChipLogProgress(DeviceLayer, "Started SMP service BLE advertising"); |
| mIsAdvertisingEnabled = true; |
| } |
| } |
| |
| void DFUOverSMP::OnBleDisconnect(struct bt_conn * conId, uint8_t reason) |
| { |
| PlatformMgr().LockChipStack(); |
| |
| // After BLE disconnect SMP advertising needs to be restarted. Before making it ensure that BLE disconnect was not triggered |
| // by closing CHIPoBLE service connection (in that case CHIPoBLE advertising needs to be restarted). |
| if (!ConnectivityMgr().IsBLEAdvertisingEnabled() && (ConnectivityMgr().NumBLEConnections() == 0)) |
| { |
| sDFUOverSMP.restartAdvertisingCallback(); |
| } |
| |
| PlatformMgr().UnlockChipStack(); |
| } |
| |
| void DFUOverSMP::ChipEventHandler(const ChipDeviceEvent * event, intptr_t /* arg */) |
| { |
| if (!GetDFUOverSMP().IsEnabled()) |
| return; |
| |
| switch (event->Type) |
| { |
| case DeviceEventType::kCHIPoBLEAdvertisingChange: |
| if (event->CHIPoBLEAdvertisingChange.Result == kActivity_Stopped) |
| { |
| // Check if CHIPoBLE advertising was stopped permanently or it just a matter of opened BLE connection. |
| if (ConnectivityMgr().NumBLEConnections() == 0) |
| sDFUOverSMP.restartAdvertisingCallback(); |
| } |
| break; |
| case DeviceEventType::kCHIPoBLEConnectionClosed: |
| // Check if after closing CHIPoBLE connection advertising is working, if no start SMP advertising. |
| if (!ConnectivityMgr().IsBLEAdvertisingEnabled()) |
| { |
| sDFUOverSMP.restartAdvertisingCallback(); |
| } |
| break; |
| default: |
| break; |
| } |
| } |