blob: 629bb8bc894db5640e079821baee2198e3f73e77 [file] [log] [blame]
/*
*
* 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;
}
}