blob: 2f6014e028459b98cc40e146b535e530446c19f9 [file] [log] [blame]
// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "modemfwd/modem_flasher.h"
#include <algorithm>
#include <memory>
#include <utility>
#include <vector>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/stl_util.h>
#include <chromeos/switches/modemfwd_switches.h>
#include <dbus/modemfwd/dbus-constants.h>
#include "modemfwd/firmware_file.h"
#include "modemfwd/logging.h"
#include "modemfwd/modem.h"
#include "modemfwd/notification_manager.h"
namespace modemfwd {
namespace {
class InhibitMode {
public:
explicit InhibitMode(Modem* modem) : modem_(modem) {
if (!modem_->SetInhibited(true))
ELOG(INFO) << "Inhibiting failed";
}
~InhibitMode() {
if (!modem_->SetInhibited(false))
ELOG(INFO) << "Uninhibiting failed";
}
private:
Modem* modem_;
};
} // namespace
ModemFlasher::ModemFlasher(FirmwareDirectory* firmware_directory,
std::unique_ptr<Journal> journal,
NotificationManager* notification_mgr)
: journal_(std::move(journal)),
firmware_directory_(firmware_directory),
notification_mgr_(notification_mgr) {}
base::OnceClosure ModemFlasher::TryFlashForTesting(Modem* modem,
const std::string& variant) {
firmware_directory_->OverrideVariantForTesting(variant);
return TryFlash(modem);
}
base::OnceClosure ModemFlasher::TryFlash(Modem* modem) {
std::string equipment_id = modem->GetEquipmentId();
FlashState* flash_state = &modem_info_[equipment_id];
if (!flash_state->ShouldFlash()) {
LOG(ERROR) << "Modem with equipment ID \"" << equipment_id
<< "\" failed to flash too many times; not flashing";
notification_mgr_->NotifyUpdateFirmwareCompletedFailure(
kErrorResultFlashFailure);
return base::OnceClosure();
}
std::string device_id = modem->GetDeviceId();
std::string current_carrier = modem->GetCarrierId();
// The real carrier ID before it might be replaced by the generic one
std::string real_carrier = current_carrier;
flash_state->OnCarrierSeen(current_carrier);
FirmwareDirectory::Files files = firmware_directory_->FindFirmware(
device_id, current_carrier.empty() ? nullptr : &current_carrier);
// Clear the attach APN if needed for a specific modem/carrier combination.
if (!real_carrier.empty() && !modem->ClearAttachAPN(real_carrier))
ELOG(INFO) << "Clear attach APN failed for current carrier.";
std::vector<FirmwareConfig> flash_cfg;
std::map<std::string, std::unique_ptr<FirmwareFile>> flash_files;
// Check if we need to update the main firmware.
if (flash_state->ShouldFlashMainFirmware() &&
files.main_firmware.has_value()) {
const FirmwareFileInfo& file_info = files.main_firmware.value();
ELOG(INFO) << "Found main firmware blob " << file_info.version
<< ", currently installed main firmware version: "
<< modem->GetMainFirmwareVersion();
if (file_info.version == modem->GetMainFirmwareVersion()) {
// We don't need to check the main firmware again if there's nothing new.
// Pretend that we successfully flashed it.
flash_state->OnFlashedMainFirmware();
} else {
auto firmware_file = std::make_unique<FirmwareFile>();
if (!firmware_file->PrepareFrom(firmware_directory_->GetFirmwarePath(),
file_info))
return base::OnceClosure();
// We found different firmware!
// record to flash the main firmware binary.
flash_cfg.push_back(
{kFwMain, firmware_file->path_on_filesystem(), file_info.version});
flash_files[kFwMain] = std::move(firmware_file);
}
}
// Check if we need to update the OEM firmware.
if (flash_state->ShouldFlashOemFirmware() && files.oem_firmware.has_value()) {
const FirmwareFileInfo& file_info = files.oem_firmware.value();
ELOG(INFO) << "Found OEM firmware blob " << file_info.version
<< ", currently installed OEM firmware version: "
<< modem->GetOemFirmwareVersion();
if (file_info.version == modem->GetOemFirmwareVersion()) {
flash_state->OnFlashedOemFirmware();
} else {
auto firmware_file = std::make_unique<FirmwareFile>();
if (!firmware_file->PrepareFrom(firmware_directory_->GetFirmwarePath(),
file_info))
return base::OnceClosure();
flash_cfg.push_back(
{kFwOem, firmware_file->path_on_filesystem(), file_info.version});
flash_files[kFwOem] = std::move(firmware_file);
}
}
// Check if we need to update the carrier firmware.
if (!current_carrier.empty() && files.carrier_firmware.has_value() &&
flash_state->ShouldFlashCarrierFirmware(
firmware_directory_->GetFirmwarePath().Append(
files.carrier_firmware.value().firmware_path))) {
const FirmwareFileInfo& file_info = files.carrier_firmware.value();
ELOG(INFO) << "Found carrier firmware blob " << file_info.version
<< " for carrier " << current_carrier;
// Carrier firmware operates a bit differently. We need to flash if
// the carrier or the version has changed, or if there wasn't any carrier
// firmware to begin with.
std::string carrier_fw_id = modem->GetCarrierFirmwareId();
std::string carrier_fw_version = modem->GetCarrierFirmwareVersion();
bool has_carrier_fw =
!(carrier_fw_id.empty() || carrier_fw_version.empty());
if (has_carrier_fw) {
ELOG(INFO) << "Currently installed carrier firmware version "
<< carrier_fw_version << " for carrier " << carrier_fw_id;
} else {
ELOG(INFO) << "No carrier firmware is currently installed";
}
if (!has_carrier_fw ||
!firmware_directory_->IsUsingSameFirmware(device_id, carrier_fw_id,
current_carrier) ||
carrier_fw_version != file_info.version) {
auto firmware_file = std::make_unique<FirmwareFile>();
if (!firmware_file->PrepareFrom(firmware_directory_->GetFirmwarePath(),
file_info))
return base::OnceClosure();
flash_cfg.push_back(
{kFwCarrier, firmware_file->path_on_filesystem(), file_info.version});
flash_files[kFwCarrier] = std::move(firmware_file);
}
} else {
// Log why we are not flashing the carrier firmware for debug
if (current_carrier.empty()) {
ELOG(INFO) << "No carrier found. Is a SIM card inserted?";
} else if (!files.carrier_firmware.has_value()) {
// Check if we have carrier firmware matching the SIM's carrier. If not,
// there's nothing to flash.
ELOG(INFO) << "No carrier firmware found for carrier " << current_carrier;
} else {
// ShouldFlashCarrierFirmware() was false
ELOG(INFO) << "Already flashed carrier firmware for " << current_carrier;
}
}
// Flash if we have new firmwares
if (flash_cfg.empty()) {
// This message is used by tests to track the end of flashing.
LOG(INFO) << "The modem already has the correct firmware installed";
notification_mgr_->NotifyUpdateFirmwareCompletedSuccess();
return base::OnceClosure();
}
std::vector<std::string> fw_types;
std::transform(flash_cfg.begin(), flash_cfg.end(),
std::back_inserter(fw_types),
[](const FirmwareConfig& cfg) { return cfg.fw_type; });
InhibitMode _inhibit(modem);
journal_->MarkStartOfFlashingFirmware(fw_types, device_id, current_carrier);
if (!modem->FlashFirmwares(flash_cfg)) {
flash_state->OnFlashFailed();
journal_->MarkEndOfFlashingFirmware(device_id, current_carrier);
return base::OnceClosure();
}
for (const auto& info : flash_cfg) {
std::string fw_type = info.fw_type;
base::FilePath path_for_logging = flash_files[fw_type]->path_for_logging();
if (fw_type == kFwMain)
flash_state->OnFlashedMainFirmware();
else if (fw_type == kFwOem)
flash_state->OnFlashedOemFirmware();
else if (fw_type == kFwCarrier)
flash_state->OnFlashedCarrierFirmware(path_for_logging);
ELOG(INFO) << "Flashed " << fw_type << " firmware (" << path_for_logging
<< ") to the modem";
}
return base::BindOnce(&Journal::MarkEndOfFlashingFirmware,
base::Unretained(journal_.get()), device_id,
current_carrier);
}
} // namespace modemfwd