blob: 3fe5fe5b3c7e869cc7b78cb2153188879500128f [file] [log] [blame]
// Copyright 2019 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 "model_two_device.h"
#include <fcntl.h>
#include <archive.h>
#include <archive_entry.h>
#include <thread>
#include <base/files/file.h>
#include <base/files/file_util.h>
#include "utilities.h"
namespace {
const char kImagePath[5][24] = {"update/cx3uvc.img",
"update/RS_M12MO.bin",
"update/strange_100.bin",
"update/hawkeye_342.bin",
"update/boot_fprog.boot",
};
const char kFwVideoName[] = "video.dat";
constexpr unsigned int kAVerReadDeviceVersionMaxRetry = 3;
constexpr unsigned int kAVerReadDeviceVersionRetryIntervalMs = 500;
constexpr unsigned int kReportIdCustomizeCmd = 0x08;
constexpr unsigned int kMaxDataSize = 1024;
constexpr unsigned int kReadDataSize = 16;
constexpr unsigned int kAVerRebootingWaitSecond = 60;
constexpr unsigned int kMcuStrangeI2cAddr = (0x2c << 1);
constexpr unsigned int kMcuHawkeyeI2cAddr = (0x2e << 1);
constexpr unsigned int kHawkeyeFwChecksumCount = 256 * 1024 - 256;
constexpr unsigned int kFwFlowHawkeyeSelect = 0;
constexpr unsigned int kReportIdCustomizeAck = 0x09;
constexpr unsigned int kAVerDefaultImageBlockSize = 512;
constexpr unsigned int kDeviceFwNumFive = 5;
constexpr unsigned int kDeviceFwNumTwo = 2;
} // namespace
ModelTwoDevice::ModelTwoDevice(const std::string& device_path)
: UsbDevice(),
device_path_(device_path),
file_descriptor_(-1) {}
ModelTwoDevice::~ModelTwoDevice() {
if (file_descriptor_.is_valid())
file_descriptor_.reset();
}
AverStatus ModelTwoDevice::OpenDevice() {
file_descriptor_ = CreateFd(device_path_.c_str());
if (file_descriptor_ == -1) {
PLOG(ERROR) << "Open video fd fail";
return AverStatus::OPEN_HID_DEVICE_FAILED;
}
return AverStatus::NO_ERROR;
}
AverStatus ModelTwoDevice::GetDeviceVersion(std::string* device_version) {
AverStatus status = AverStatus::UNKNOWN;
std::string version;
int attempts_count = 1;
while (true) {
status = ReadDeviceVersion();
if (status == AverStatus::NO_ERROR ||
attempts_count > kAVerReadDeviceVersionMaxRetry) {
break;
}
LOG(ERROR) << "Failed to read device version, will retry.";
attempts_count += 1;
std::this_thread::sleep_for(
std::chrono::milliseconds(kAVerReadDeviceVersionRetryIntervalMs));
}
if (status != AverStatus::NO_ERROR) {
LOG(ERROR) << "Failed to read device version after "
<< kAVerReadDeviceVersionMaxRetry << " retries";
return status;
}
device_version->assign(reinterpret_cast<char*>(&hid_info_.dat));
return AverStatus::NO_ERROR;
}
AverStatus ModelTwoDevice::ReadDeviceVersion() {
AverStatus status;
ModelTwoDeviceOutReport customize_cmd;
memset(&customize_cmd, 0, sizeof(customize_cmd));
customize_cmd.id = kReportIdCustomizeCmd;
customize_cmd.cmd = HID_CSTM_CMD_FACTORY_GET_FW_VERSION;
customize_cmd.parm[0] = 0;
customize_cmd.parm[1] = 0;
status = SendHidVerTwoControl(customize_cmd);
if (status != AverStatus::NO_ERROR)
return status;
return AverStatus::NO_ERROR;
}
AverStatus ModelTwoDevice::SendHidVerTwoControl(
const struct ModelTwoDeviceOutReport& customize_cmd) {
int rt = HANDLE_EINTR(write(file_descriptor_.get(),
&customize_cmd,
kMaxDataSize - 1));
if (rt < 0) {
LOG(ERROR) << "Write hid cmd failed.";
return AverStatus::WRITE_DEVICE_FAILED;
}
rt = HANDLE_EINTR(read(file_descriptor_.get(), &hid_info_, kReadDataSize));
if (rt < 0) {
LOG(ERROR) << "Read hid msg failed.";
return AverStatus::READ_DEVICE_FAILED;
}
if (hid_info_.id != kReportIdCustomizeAck) {
LOG(ERROR) << "Read hid msg failed";
return AverStatus::READ_DEVICE_FAILED;
}
return AverStatus::NO_ERROR;
}
AverStatus ModelTwoDevice::LoadFirmwareToBuffer() {
AverStatus status = HandleCompressedFirmware();
for (int i = 0; i < device_fw_num_; i++) {
base::FilePath file_path = temp_path_.Append(kImagePath[i]);
if (!ReadFirmwareFileToBuffer(file_path, &firmware_buffer_[i])) {
base::DeletePathRecursively(temp_path_);
return AverStatus::READ_FW_TO_BUF_FAILED;
}
}
if (!base::DeletePathRecursively(temp_path_))
LOG(ERROR) << "Failed to delete untar firmware.";
return status;
}
AverStatus ModelTwoDevice::HandleCompressedFirmware() {
// Check more device firmwares or not.
bool ok = false;
std::string video_fw_tmp;
std::string ver = GetVersion(firmware_version_);
video_fw_tmp = ver + kFwVideoName;
ok = FindIntermediateCompressedFile(temp_path_, video_fw_tmp);
if (ok) {
base::FilePath video_file_path = temp_path_.Append(video_fw_tmp);
ok = ExtractTarFile(temp_path_, video_file_path.value().c_str());
device_fw_num_ = kDeviceFwNumFive;
} else
device_fw_num_ = kDeviceFwNumTwo;
return AverStatus::NO_ERROR;
}
AverStatus ModelTwoDevice::PerformUpdate(const base::FilePath& tmp_path) {
temp_path_ = tmp_path;
AverStatus status = LoadFirmwareToBuffer();
if (status != AverStatus::NO_ERROR)
return status;
status = FirmwareUpdate();
return status;
}
AverStatus ModelTwoDevice::FirmwareUpdate() {
AverStatus status;
uint8_t cmd;
uint32_t i2c_address;
for (int i = 0; i < device_fw_num_; i++) {
device_update_[i] = false;
switch (i) {
case M12MO_FW_UPLOAD:
cmd = HID_CSTM_CMD_M12MO_COMPARE_CHECKSUM;
i2c_address = 0;
break;
case HAWKEYE_FW_UPLOAD:
cmd = HID_CSTM_CMD_FLASH_COMPARE_CHECKSUM;
i2c_address = kMcuHawkeyeI2cAddr;
case M051_FW_UPLOAD:
cmd = HID_CSTM_CMD_FLASH_COMPARE_CHECKSUM;
i2c_address = kMcuStrangeI2cAddr;
break;
case CX3_FW_UPLOAD:
case BOOTIMG_FW_UPLOAD:
device_update_[i] = true;
continue;
break;
}
status = IspCompareChecksum(cmd, i2c_address, 0, firmware_buffer_[i]);
if (status == AverStatus::NO_ERROR) {
LOG(INFO) << "Firmware checksum is not the same. Update Device:" << i;
device_update_[i] = true;
} else if (status == AverStatus::CHECKSUM_SAME) {
LOG(INFO) << "Firmware checksum is the same. Skip device:" << i;
} else if (status != AverStatus::NO_ERROR) {
LOG(ERROR) << "Checksum failed. Device:" << i;
return status;
}
}
status = IspSupport();
if (status != AverStatus::NO_ERROR) {
LOG(ERROR) << "Isp support failed.";
return status;
}
for (int i = 0; i < device_fw_num_; i++) {
status = FirmwareUpload(i);
if (status != AverStatus::NO_ERROR)
return status;
}
status = IspUpdate();
if (status != AverStatus::NO_ERROR)
return status;
LOG(INFO) << "Devices updating! No power off!";
std::this_thread::sleep_for(
std::chrono::seconds(kAVerRebootingWaitSecond * device_fw_num_));
LOG(INFO) << "Isp update finish. Device will reboot!";
return AverStatus::NO_ERROR;
}
AverStatus ModelTwoDevice::IspCompareChecksum(uint8_t cmd,
uint32_t i2c_address,
uint32_t chip_select,
const std::vector<char>& buffer) {
checksum_ = 0;
for (auto it = buffer.begin(); it != buffer.end(); it++) {
if (i2c_address == kMcuHawkeyeI2cAddr &&
distance(buffer.begin(), it) >= kHawkeyeFwChecksumCount)
break;
else
checksum_ += static_cast<unsigned char>(*it);
}
ModelTwoDeviceOutReport customize_cmd;
memset(&customize_cmd, 0, sizeof(customize_cmd));
customize_cmd.id = kReportIdCustomizeCmd;
customize_cmd.cmd = cmd;
if (cmd == HID_CSTM_CMD_SAFE_ISP_UPLOAD_COMPARE_CHECKSUM) {
customize_cmd.parm[0] = chip_select;
customize_cmd.parm[1] = checksum_;
} else {
customize_cmd.parm[0] = checksum_;
customize_cmd.parm[1] = i2c_address;
}
AverStatus status = SendHidVerTwoControl(customize_cmd);
if (status != AverStatus::NO_ERROR) {
LOG(ERROR) << "Failed to send hid command.";
return status;
}
if (hid_info_.cmd == HID_ACK_COMPARE_SAME)
return AverStatus::CHECKSUM_SAME;
return AverStatus::NO_ERROR;
}
AverStatus ModelTwoDevice::IspSupport() {
ModelTwoDeviceOutReport customize_cmd;
memset(&customize_cmd, 0, sizeof(customize_cmd));
customize_cmd.id = kReportIdCustomizeCmd;
customize_cmd.cmd = HID_CSTM_CMD_SAFE_ISP_SUPPORT;
AverStatus status = SendHidVerTwoControl(customize_cmd);
if (status != AverStatus::NO_ERROR) {
LOG(ERROR) << "Failed to send hid command.";
return status;
}
if (hid_info_.cmd != HID_ACK_SAFE_ISP_SUPPORT) {
LOG(ERROR) << "Can't support new firmware update progress.";
return AverStatus::FAILED_SUPPORT_NEW_FW_UPDATE;
}
return AverStatus::NO_ERROR;
}
AverStatus ModelTwoDevice::IspUpdate() {
ModelTwoDeviceOutReport customize_cmd;
memset(&customize_cmd, 0, sizeof(customize_cmd));
customize_cmd.id = kReportIdCustomizeCmd;
customize_cmd.cmd = HID_CSTM_CMD_SAFE_ISP_UPDATA_START;
customize_cmd.parm[0] = (device_update_[0] << 0) | (device_update_[1] << 1) |
(device_update_[2] << 2) | (device_update_[3] << 3);
AverStatus status = SendHidVerTwoControl(customize_cmd);
if (status != AverStatus::NO_ERROR) {
LOG(ERROR) << "Failed to send hid command.";
return status;
}
return AverStatus::NO_ERROR;
}
AverStatus ModelTwoDevice::FirmwareUpload(int index) {
if (device_update_[index] == false)
return AverStatus::NO_ERROR;
AverStatus status = IspPrepare(HID_CSTM_CMD_SAFE_ISP_UPLOAD_PREPARE,
index, 0, firmware_buffer_[index].size());
if (status != AverStatus::NO_ERROR) {
LOG(ERROR) << "Isp prepare failed.";
return status;
}
status = IspErase(HID_CSTM_CMD_SAFE_ISP_ERASE_TEMP, index);
if (status != AverStatus::NO_ERROR) {
LOG(ERROR) << "Isp erase failed.";
return status;
}
status = IspUpload(HID_CSTM_CMD_SAFE_ISP_UPLOAD_TO_CX3 + index,
firmware_buffer_[index],
firmware_buffer_[index].size(),
kAVerDefaultImageBlockSize,
kFwFlowHawkeyeSelect);
if (status != AverStatus::NO_ERROR) {
LOG(ERROR) << "Isp upload failed.";
return status;
}
status = IspCompareChecksum(HID_CSTM_CMD_SAFE_ISP_UPLOAD_COMPARE_CHECKSUM, 0,
index, firmware_buffer_[index]);
if (status == AverStatus::CHECKSUM_SAME) {
LOG(INFO) << "Firmware in the flash is correct.";
device_update_[index] = true;
} else if (status != AverStatus::NO_ERROR) {
LOG(ERROR) << "Checksum failed.";
return status;
}
return AverStatus::NO_ERROR;
}
AverStatus ModelTwoDevice::IspPrepare(uint8_t cmd,
uint32_t chip_select,
uint32_t sector_count,
uint32_t file_size) {
ModelTwoDeviceOutReport customize_cmd;
memset(&customize_cmd, 0, sizeof(customize_cmd));
customize_cmd.id = kReportIdCustomizeCmd;
customize_cmd.cmd = cmd;
if (cmd == HID_CSTM_CMD_M051_FLASH_PREPARE ||
cmd == HID_CSTM_CMD_SAFE_ISP_UPLOAD_PREPARE) {
customize_cmd.parm[0] = chip_select;
customize_cmd.parm[1] = file_size;
} else
customize_cmd.parm[0] = sector_count;
AverStatus status = SendHidVerTwoControl(customize_cmd);
if (status != AverStatus::NO_ERROR) {
LOG(ERROR) << "Failed to send hid command. Command:" << cmd;
return status;
}
return AverStatus::NO_ERROR;
}
AverStatus ModelTwoDevice::IspErase(uint8_t cmd, uint32_t chip_select) {
ModelTwoDeviceOutReport customize_cmd;
memset(&customize_cmd, 0, sizeof(customize_cmd));
customize_cmd.id = kReportIdCustomizeCmd;
customize_cmd.cmd = cmd;
customize_cmd.parm[0] = chip_select;
AverStatus status = SendHidVerTwoControl(customize_cmd);
if (status != AverStatus::NO_ERROR) {
LOG(ERROR) << "Failed to send hid command. Command:" << cmd;
return status;
}
return AverStatus::NO_ERROR;
}
AverStatus ModelTwoDevice::IspUpload(uint8_t cmd,
const std::vector<char>& buffer,
uint32_t isp_file_size,
uint32_t chip_block_size,
uint32_t upload_select) {
ModelTwoDeviceOutReport customize_cmd;
uint32_t offset = 0;
while (isp_file_size > 0) {
unsigned int block_size =
std::min<unsigned int>(isp_file_size, chip_block_size);
memset(&customize_cmd, 0, sizeof(customize_cmd));
customize_cmd.id = kReportIdCustomizeCmd;
customize_cmd.cmd = cmd;
customize_cmd.parm[0] = offset;
customize_cmd.parm[1] = block_size;
std::copy(buffer.cbegin() + offset, buffer.cbegin() + offset + block_size,
customize_cmd.dat);
AverStatus status = SendHidVerTwoControl(customize_cmd);
if (status != AverStatus::NO_ERROR) {
LOG(ERROR) << "Failed to send hid command. Command:" << cmd;
return status;
}
offset += block_size;
isp_file_size -= block_size;
}
return AverStatus::NO_ERROR;
}