blob: 6bb252ae8e312e902c98d7e75a25575471f92d0a [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 <memory>
#include <utility>
#include <base/files/file_util.h>
#include <base/stl_util.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
#include "modemfwd/firmware_file.h"
#include "modemfwd/logging.h"
#include "modemfwd/modem.h"
namespace {
constexpr char kDisableAutoUpdatePref[] =
"/var/lib/modemfwd/disable_auto_update";
} // namespace
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
bool IsAutoUpdateDisabledByPref() {
const base::FilePath pref_path(kDisableAutoUpdatePref);
std::string contents;
if (!base::ReadFileToString(pref_path, &contents))
return false;
contents = base::TrimWhitespaceASCII(contents, base::TRIM_ALL).as_string();
int pref_value;
if (!base::StringToInt(contents, &pref_value))
return false;
return (pref_value == 1);
}
ModemFlasher::ModemFlasher(
std::unique_ptr<FirmwareDirectory> firmware_directory,
std::unique_ptr<Journal> journal)
: firmware_directory_(std::move(firmware_directory)),
journal_(std::move(journal)) {}
base::Closure ModemFlasher::TryFlash(Modem* modem) {
if (IsAutoUpdateDisabledByPref()) {
LOG(INFO) << "Update disabled by pref";
return base::Closure();
}
std::string equipment_id = modem->GetEquipmentId();
FlashState* flash_state = &modem_info_[equipment_id];
if (!flash_state->ShouldFlash()) {
LOG(WARNING) << "Modem with equipment ID \"" << equipment_id
<< "\" failed to flash too many times; not flashing";
return base::Closure();
}
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);
// 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 {
FirmwareFile firmware_file;
if (!firmware_file.PrepareFrom(file_info))
return base::Closure();
// We found different firmware! Flash the modem, and since it will
// reboot afterwards, we can wait to get called again to check the
// carrier firmware.
InhibitMode _inhibit(modem);
journal_->MarkStartOfFlashingMainFirmware(device_id, current_carrier);
if (modem->FlashMainFirmware(firmware_file.path_on_filesystem(),
file_info.version)) {
// Refer to |firmware_file.path_for_logging()| in the log and journal.
flash_state->OnFlashedMainFirmware();
ELOG(INFO) << "Flashed " << firmware_file.path_for_logging().value()
<< " to the modem";
return base::Bind(&Journal::MarkEndOfFlashingMainFirmware,
base::Unretained(journal_.get()), device_id,
current_carrier);
} else {
flash_state->OnFlashFailed();
journal_->MarkEndOfFlashingMainFirmware(device_id, current_carrier);
return base::Closure();
}
}
}
// If there's no SIM, we can stop here.
if (current_carrier.empty()) {
ELOG(INFO) << "No carrier found. Is a SIM card inserted?";
return base::Closure();
}
// Clear the attach APN if needed for a specific modem/carrier combination.
if (!modem->ClearAttachAPN(real_carrier)) {
ELOG(INFO) << "Clear attach APN failed for current carrier.";
}
// Check if we have carrier firmware matching the SIM's carrier. If not,
// there's nothing to flash.
if (!files.carrier_firmware.has_value()) {
ELOG(INFO) << "No carrier firmware found for carrier " << current_carrier;
return base::Closure();
}
const FirmwareFileInfo& file_info = files.carrier_firmware.value();
if (!flash_state->ShouldFlashCarrierFirmware(file_info.firmware_path)) {
ELOG(INFO) << "Already flashed carrier firmware for " << current_carrier;
return base::Closure();
}
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 || carrier_fw_id != current_carrier ||
carrier_fw_version != file_info.version) {
FirmwareFile firmware_file;
if (!firmware_file.PrepareFrom(file_info))
return base::Closure();
InhibitMode _inhibit(modem);
journal_->MarkStartOfFlashingCarrierFirmware(device_id, current_carrier);
if (modem->FlashCarrierFirmware(firmware_file.path_on_filesystem(),
file_info.version)) {
// Refer to |firmware_file.path_for_logging()| in the log and journal.
flash_state->OnFlashedCarrierFirmware(firmware_file.path_for_logging());
ELOG(INFO) << "Flashed " << firmware_file.path_for_logging().value()
<< " to the modem";
return base::Bind(&Journal::MarkEndOfFlashingCarrierFirmware,
base::Unretained(journal_.get()), device_id,
current_carrier);
} else {
flash_state->OnFlashFailed();
journal_->MarkEndOfFlashingCarrierFirmware(device_id, current_carrier);
return base::Closure();
}
}
return base::Closure();
}
} // namespace modemfwd