886 lines
34 KiB
C++
886 lines
34 KiB
C++
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
// Licensed under the MIT License.
|
||
|
|
||
|
#include "firmware_helper.h"
|
||
|
#include <utcommon.h>
|
||
|
|
||
|
#include <k4ainternal/logging.h>
|
||
|
#include <k4ainternal/usbcommand.h>
|
||
|
|
||
|
#include <azure_c_shared_utility/tickcounter.h>
|
||
|
#include <azure_c_shared_utility/threadapi.h>
|
||
|
|
||
|
#define K4A_TEST_VERIFY_SUCCEEDED(_result_) \
|
||
|
if (K4A_FAILED(TRACE_CALL(_result_))) \
|
||
|
{ \
|
||
|
return K4A_RESULT_FAILED; \
|
||
|
}
|
||
|
|
||
|
#define K4A_TEST_VERIFY_TRUE(_condition_) \
|
||
|
if (!(_condition_)) \
|
||
|
{ \
|
||
|
LOG_ERROR("\"" #_condition_ "\" was false but was expected to be true."); \
|
||
|
return K4A_RESULT_FAILED; \
|
||
|
}
|
||
|
|
||
|
#define K4A_TEST_VERIFY_LE(_val1_, _val2_) \
|
||
|
if ((_val1_) > (_val2_)) \
|
||
|
{ \
|
||
|
LOG_ERROR("\"" #_val1_ "\" > \"" #_val2_ "\" but was expected to be less than or equal."); \
|
||
|
std::cout << "\"" #_val1_ "\" = " << (_val1_) << " \"" #_val2_ "\" = " << (_val2_) << std::endl; \
|
||
|
return K4A_RESULT_FAILED; \
|
||
|
}
|
||
|
|
||
|
#define K4A_TEST_VERIFY_EQUAL(_val1_, _val2_) \
|
||
|
if ((_val1_) != (_val2_)) \
|
||
|
{ \
|
||
|
LOG_ERROR("\"" #_val1_ "\" != \"" #_val2_ "\" but was expected to be equal."); \
|
||
|
std::cout << "\"" #_val1_ "\" = " << (_val1_) << " \"" #_val2_ "\" = " << (_val2_) << std::endl; \
|
||
|
return K4A_RESULT_FAILED; \
|
||
|
}
|
||
|
|
||
|
#define K4A_TEST_VERIFY_NOT_EQUAL(_val1_, _val2_) \
|
||
|
if ((_val1_) == (_val2_)) \
|
||
|
{ \
|
||
|
LOG_ERROR("\"" #_val1_ "\" == \"" #_val2_ "\" but was expected to not be equal."); \
|
||
|
std::cout << "\"" #_val1_ "\" = " << (_val1_) << " \"" #_val2_ "\" = " << (_val2_) << std::endl; \
|
||
|
return K4A_RESULT_FAILED; \
|
||
|
}
|
||
|
|
||
|
int g_k4a_port_number = -1;
|
||
|
connection_exerciser *g_connection_exerciser = nullptr;
|
||
|
char *g_serial_number;
|
||
|
|
||
|
char *g_candidate_firmware_path = nullptr;
|
||
|
uint8_t *g_candidate_firmware_buffer = nullptr;
|
||
|
size_t g_candidate_firmware_size = 0;
|
||
|
firmware_package_info_t g_candidate_firmware_package_info = { 0 };
|
||
|
|
||
|
char *g_test_firmware_path = nullptr;
|
||
|
uint8_t *g_test_firmware_buffer = nullptr;
|
||
|
size_t g_test_firmware_size = 0;
|
||
|
firmware_package_info_t g_test_firmware_package_info = { 0 };
|
||
|
|
||
|
char *g_lkg_firmware_path = nullptr;
|
||
|
uint8_t *g_lkg_firmware_buffer = nullptr;
|
||
|
size_t g_lkg_firmware_size = 0;
|
||
|
firmware_package_info_t g_lkg_firmware_package_info = { 0 };
|
||
|
|
||
|
char *g_factory_firmware_path = nullptr;
|
||
|
uint8_t *g_factory_firmware_buffer = nullptr;
|
||
|
size_t g_factory_firmware_size = 0;
|
||
|
firmware_package_info_t g_factory_firmware_package_info = { 0 };
|
||
|
|
||
|
static bool common_initialized = false;
|
||
|
|
||
|
k4a_result_t setup_common_test()
|
||
|
{
|
||
|
int port;
|
||
|
float voltage;
|
||
|
float current;
|
||
|
|
||
|
if (common_initialized)
|
||
|
{
|
||
|
return K4A_RESULT_SUCCEEDED;
|
||
|
}
|
||
|
|
||
|
if (g_candidate_firmware_path == nullptr)
|
||
|
{
|
||
|
std::cout << "The firmware path setting is required and wasn't supplied.\n\n";
|
||
|
return K4A_RESULT_FAILED;
|
||
|
}
|
||
|
|
||
|
g_k4a_port_number = -1;
|
||
|
g_connection_exerciser = new (std::nothrow) connection_exerciser();
|
||
|
|
||
|
std::cout << "Searching for the connection exerciser and device..." << std::endl;
|
||
|
LOG_INFO("Searching for the connection exerciser...", 0);
|
||
|
K4A_TEST_VERIFY_SUCCEEDED(g_connection_exerciser->find_connection_exerciser());
|
||
|
std::cout << "Found it, searching ports\n";
|
||
|
K4A_TEST_VERIFY_SUCCEEDED(g_connection_exerciser->set_usb_port(0));
|
||
|
|
||
|
LOG_INFO("Searching for device...", 0);
|
||
|
|
||
|
for (int i = 0; i < CONN_EX_MAX_NUM_PORTS; ++i)
|
||
|
{
|
||
|
std::cout << "Inspecting port " << i << "\n";
|
||
|
K4A_TEST_VERIFY_SUCCEEDED(g_connection_exerciser->set_usb_port(i));
|
||
|
port = g_connection_exerciser->get_usb_port();
|
||
|
K4A_TEST_VERIFY_EQUAL(port, i);
|
||
|
|
||
|
voltage = g_connection_exerciser->get_voltage_reading();
|
||
|
K4A_TEST_VERIFY_NOT_EQUAL(voltage, -1);
|
||
|
current = g_connection_exerciser->get_current_reading();
|
||
|
K4A_TEST_VERIFY_NOT_EQUAL(current, -1);
|
||
|
|
||
|
if (current < 0)
|
||
|
{
|
||
|
current = current * -1;
|
||
|
}
|
||
|
|
||
|
if (voltage > 4.5 && voltage < 5.5 && current > 0.1)
|
||
|
{
|
||
|
if (g_k4a_port_number != -1)
|
||
|
{
|
||
|
std::cout << "More than one device was detected on the connection exerciser." << std::endl;
|
||
|
return K4A_RESULT_FAILED;
|
||
|
}
|
||
|
std::cout << "Found a device on port " << i << "\n";
|
||
|
g_k4a_port_number = port;
|
||
|
}
|
||
|
|
||
|
LOG_INFO("On port #%d: %4.2fV %4.2fA\n", port, voltage, current);
|
||
|
}
|
||
|
|
||
|
if (g_k4a_port_number == -1)
|
||
|
{
|
||
|
std::cout << "The Azure Kinect DK was not detected on any port of the connection exerciser." << std::endl;
|
||
|
return K4A_RESULT_FAILED;
|
||
|
}
|
||
|
|
||
|
// Get the serial number of the connected device
|
||
|
{
|
||
|
depthmcu_t depthmcu;
|
||
|
size_t serial_number_size = 0;
|
||
|
std::cout << "Reading serial number of Azure Kinect\n";
|
||
|
K4A_TEST_VERIFY_SUCCEEDED(g_connection_exerciser->set_usb_port(g_k4a_port_number));
|
||
|
ThreadAPI_Sleep(3000);
|
||
|
K4A_TEST_VERIFY_EQUAL(K4A_RESULT_SUCCEEDED, depthmcu_create(K4A_DEVICE_DEFAULT, &depthmcu));
|
||
|
K4A_TEST_VERIFY_EQUAL(K4A_BUFFER_RESULT_TOO_SMALL,
|
||
|
depthmcu_get_serialnum(depthmcu, nullptr, &serial_number_size));
|
||
|
K4A_TEST_VERIFY_NOT_EQUAL(NULL, g_serial_number = (char *)malloc(serial_number_size));
|
||
|
K4A_TEST_VERIFY_EQUAL(K4A_BUFFER_RESULT_SUCCEEDED,
|
||
|
depthmcu_get_serialnum(depthmcu, g_serial_number, &serial_number_size));
|
||
|
depthmcu_destroy(depthmcu);
|
||
|
std::cout << "Azure Kinect S/N is " << g_serial_number << std::endl;
|
||
|
}
|
||
|
|
||
|
K4A_TEST_VERIFY_SUCCEEDED(g_connection_exerciser->set_usb_port(0));
|
||
|
|
||
|
std::cout << "Loading Release Candidate firmware package: " << g_candidate_firmware_path << std::endl;
|
||
|
load_firmware_files(g_candidate_firmware_path, &g_candidate_firmware_buffer, &g_candidate_firmware_size);
|
||
|
g_candidate_firmware_package_info.buffer = g_candidate_firmware_buffer;
|
||
|
g_candidate_firmware_package_info.size = g_candidate_firmware_size;
|
||
|
parse_firmware_package(&g_candidate_firmware_package_info);
|
||
|
|
||
|
std::cout << "Loading Test firmware package: " << g_test_firmware_path << std::endl;
|
||
|
load_firmware_files(g_test_firmware_path, &g_test_firmware_buffer, &g_test_firmware_size);
|
||
|
g_test_firmware_package_info.buffer = g_test_firmware_buffer;
|
||
|
g_test_firmware_package_info.size = g_test_firmware_size;
|
||
|
parse_firmware_package(&g_test_firmware_package_info);
|
||
|
|
||
|
std::cout << "Loading LKG firmware package: " << g_lkg_firmware_path << std::endl;
|
||
|
load_firmware_files(g_lkg_firmware_path, &g_lkg_firmware_buffer, &g_lkg_firmware_size);
|
||
|
g_lkg_firmware_package_info.buffer = g_lkg_firmware_buffer;
|
||
|
g_lkg_firmware_package_info.size = g_lkg_firmware_size;
|
||
|
parse_firmware_package(&g_lkg_firmware_package_info);
|
||
|
|
||
|
std::cout << "Loading Factory firmware package: " << g_factory_firmware_path << std::endl;
|
||
|
load_firmware_files(g_factory_firmware_path, &g_factory_firmware_buffer, &g_factory_firmware_size);
|
||
|
g_factory_firmware_package_info.buffer = g_factory_firmware_buffer;
|
||
|
g_factory_firmware_package_info.size = g_factory_firmware_size;
|
||
|
parse_firmware_package(&g_factory_firmware_package_info);
|
||
|
|
||
|
common_initialized = true;
|
||
|
return K4A_RESULT_SUCCEEDED;
|
||
|
}
|
||
|
|
||
|
void tear_down_common_test()
|
||
|
{
|
||
|
if (g_connection_exerciser != nullptr)
|
||
|
{
|
||
|
g_k4a_port_number = -1;
|
||
|
delete g_connection_exerciser;
|
||
|
g_connection_exerciser = nullptr;
|
||
|
}
|
||
|
|
||
|
if (g_test_firmware_buffer != nullptr)
|
||
|
{
|
||
|
free(g_test_firmware_buffer);
|
||
|
g_test_firmware_buffer = nullptr;
|
||
|
}
|
||
|
|
||
|
if (g_candidate_firmware_buffer != nullptr)
|
||
|
{
|
||
|
free(g_candidate_firmware_buffer);
|
||
|
g_candidate_firmware_buffer = nullptr;
|
||
|
}
|
||
|
|
||
|
if (g_lkg_firmware_buffer != nullptr)
|
||
|
{
|
||
|
free(g_lkg_firmware_buffer);
|
||
|
g_lkg_firmware_buffer = nullptr;
|
||
|
}
|
||
|
|
||
|
if (g_factory_firmware_buffer != nullptr)
|
||
|
{
|
||
|
free(g_factory_firmware_buffer);
|
||
|
g_factory_firmware_buffer = nullptr;
|
||
|
}
|
||
|
|
||
|
if (g_serial_number != nullptr)
|
||
|
{
|
||
|
free(g_serial_number);
|
||
|
g_serial_number = nullptr;
|
||
|
}
|
||
|
|
||
|
common_initialized = false;
|
||
|
}
|
||
|
|
||
|
k4a_result_t load_firmware_files(char *firmware_path, uint8_t **firmware_buffer, size_t *firmware_size)
|
||
|
{
|
||
|
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, firmware_path == NULL);
|
||
|
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, firmware_buffer == NULL);
|
||
|
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, firmware_size == NULL);
|
||
|
|
||
|
k4a_result_t result = K4A_RESULT_FAILED;
|
||
|
FILE *pFirmwareFile = NULL;
|
||
|
size_t numRead = 0;
|
||
|
uint8_t *tempFirmwareBuffer = NULL;
|
||
|
|
||
|
if ((pFirmwareFile = fopen(firmware_path, "rb")) != NULL)
|
||
|
{
|
||
|
fseek(pFirmwareFile, 0, SEEK_END);
|
||
|
size_t tempFirmwareSize = (size_t)ftell(pFirmwareFile);
|
||
|
if (tempFirmwareSize == (size_t)-1L)
|
||
|
{
|
||
|
LOG_ERROR("Failed to get size of the firmware package.", 0);
|
||
|
return K4A_RESULT_FAILED;
|
||
|
}
|
||
|
|
||
|
fseek(pFirmwareFile, 0, SEEK_SET);
|
||
|
|
||
|
tempFirmwareBuffer = (uint8_t *)malloc(tempFirmwareSize);
|
||
|
if (tempFirmwareBuffer == NULL)
|
||
|
{
|
||
|
LOG_ERROR("Failed to allocate memory.", 0);
|
||
|
return K4A_RESULT_FAILED;
|
||
|
}
|
||
|
|
||
|
LOG_INFO("Loaded: \"%s\", File size: %d bytes.", firmware_path, tempFirmwareSize);
|
||
|
numRead = fread(tempFirmwareBuffer, tempFirmwareSize, 1, pFirmwareFile);
|
||
|
fclose(pFirmwareFile);
|
||
|
|
||
|
if (numRead != 1)
|
||
|
{
|
||
|
LOG_ERROR("Failed to read all of the data from the file.", 0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*firmware_buffer = tempFirmwareBuffer;
|
||
|
*firmware_size = tempFirmwareSize;
|
||
|
tempFirmwareBuffer = NULL;
|
||
|
result = K4A_RESULT_SUCCEEDED;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
LOG_ERROR("Failed to open \"%s\" errno=%d", firmware_path, errno);
|
||
|
}
|
||
|
|
||
|
if (tempFirmwareBuffer)
|
||
|
{
|
||
|
free(tempFirmwareBuffer);
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
firmware_operation_status_t calculate_overall_component_status(const firmware_component_status_t status)
|
||
|
{
|
||
|
if (status.overall == FIRMWARE_OPERATION_SUCCEEDED)
|
||
|
{
|
||
|
return FIRMWARE_OPERATION_SUCCEEDED;
|
||
|
}
|
||
|
|
||
|
if (status.overall == FIRMWARE_OPERATION_INPROGRESS)
|
||
|
{
|
||
|
return FIRMWARE_OPERATION_INPROGRESS;
|
||
|
}
|
||
|
|
||
|
// If the version check failed, this component's update was skipped. This could because the new version is
|
||
|
// an unsafe downgrade or the versions are the same and no update is required.
|
||
|
if (status.version_check == FIRMWARE_OPERATION_FAILED)
|
||
|
{
|
||
|
return FIRMWARE_OPERATION_SUCCEEDED;
|
||
|
}
|
||
|
|
||
|
printf("Overall Component Status failed: A:%d V:%d T:%d E:%d W:%d O:%d\n",
|
||
|
status.authentication_check,
|
||
|
status.version_check,
|
||
|
status.image_transfer,
|
||
|
status.flash_erase,
|
||
|
status.flash_write,
|
||
|
status.overall);
|
||
|
|
||
|
return FIRMWARE_OPERATION_FAILED;
|
||
|
}
|
||
|
|
||
|
bool compare_version(k4a_version_t left_version, k4a_version_t right_version)
|
||
|
{
|
||
|
return left_version.major == right_version.major && left_version.minor == right_version.minor &&
|
||
|
left_version.iteration == right_version.iteration;
|
||
|
}
|
||
|
|
||
|
bool compare_version(k4a_hardware_version_t device_version, firmware_package_info_t firmware_version)
|
||
|
{
|
||
|
bool are_equal = true;
|
||
|
|
||
|
if (!compare_version(device_version.audio, firmware_version.audio))
|
||
|
{
|
||
|
printf("Audio version mismatch.\n");
|
||
|
are_equal = false;
|
||
|
}
|
||
|
|
||
|
if (!compare_version_list(device_version.depth_sensor,
|
||
|
firmware_version.depth_config_number_versions,
|
||
|
firmware_version.depth_config_versions))
|
||
|
{
|
||
|
printf("Depth sensor does not exist in package.\n");
|
||
|
are_equal = false;
|
||
|
}
|
||
|
|
||
|
if (!compare_version(device_version.depth, firmware_version.depth))
|
||
|
{
|
||
|
printf("Depth version mismatch.\n");
|
||
|
are_equal = false;
|
||
|
}
|
||
|
|
||
|
if (!compare_version(device_version.rgb, firmware_version.rgb))
|
||
|
{
|
||
|
printf("RGB version mismatch.\n");
|
||
|
are_equal = false;
|
||
|
}
|
||
|
|
||
|
return are_equal;
|
||
|
}
|
||
|
|
||
|
bool compare_version_list(k4a_version_t device_version, uint8_t count, k4a_version_t versions[5])
|
||
|
{
|
||
|
for (int i = 0; i < count; ++i)
|
||
|
{
|
||
|
if (device_version.major == versions[i].major && device_version.minor == versions[i].minor)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void log_firmware_build_config(k4a_firmware_build_t build_config)
|
||
|
{
|
||
|
std::cout << " Build Config: ";
|
||
|
switch (build_config)
|
||
|
{
|
||
|
case K4A_FIRMWARE_BUILD_RELEASE:
|
||
|
std::cout << "Production" << std::endl;
|
||
|
break;
|
||
|
|
||
|
case K4A_FIRMWARE_BUILD_DEBUG:
|
||
|
std::cout << "Debug" << std::endl;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
std::cout << "Unknown" << std::endl;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void log_firmware_signature_type(k4a_firmware_signature_t signature_type, bool certificate)
|
||
|
{
|
||
|
if (certificate)
|
||
|
{
|
||
|
std::cout << " Certificate Type: ";
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
std::cout << " Signature Type: ";
|
||
|
}
|
||
|
|
||
|
switch (signature_type)
|
||
|
{
|
||
|
case K4A_FIRMWARE_SIGNATURE_MSFT:
|
||
|
std::cout << "Microsoft" << std::endl;
|
||
|
break;
|
||
|
|
||
|
case K4A_FIRMWARE_SIGNATURE_TEST:
|
||
|
std::cout << "Test" << std::endl;
|
||
|
break;
|
||
|
|
||
|
case K4A_FIRMWARE_SIGNATURE_UNSIGNED:
|
||
|
std::cout << "Unsigned" << std::endl;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
std::cout << "Unknown (" << signature_type << ")" << std::endl;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void log_firmware_version(firmware_package_info_t firmware_version)
|
||
|
{
|
||
|
printf("Firmware Package Versions:\n");
|
||
|
printf(" RGB camera firmware: %d.%d.%d\n",
|
||
|
firmware_version.rgb.major,
|
||
|
firmware_version.rgb.minor,
|
||
|
firmware_version.rgb.iteration);
|
||
|
printf(" Depth camera firmware: %d.%d.%d\n",
|
||
|
firmware_version.depth.major,
|
||
|
firmware_version.depth.minor,
|
||
|
firmware_version.depth.iteration);
|
||
|
|
||
|
printf(" Depth config file: ");
|
||
|
for (size_t i = 0; i < firmware_version.depth_config_number_versions; i++)
|
||
|
{
|
||
|
printf("%d.%d ",
|
||
|
firmware_version.depth_config_versions[i].major,
|
||
|
firmware_version.depth_config_versions[i].minor);
|
||
|
}
|
||
|
printf("\n");
|
||
|
|
||
|
printf(" Audio firmware: %d.%d.%d\n",
|
||
|
firmware_version.audio.major,
|
||
|
firmware_version.audio.minor,
|
||
|
firmware_version.audio.iteration);
|
||
|
|
||
|
log_firmware_build_config(firmware_version.build_config);
|
||
|
|
||
|
log_firmware_signature_type(firmware_version.certificate_type, true);
|
||
|
log_firmware_signature_type(firmware_version.signature_type, false);
|
||
|
}
|
||
|
|
||
|
void log_device_version(k4a_hardware_version_t firmware_version)
|
||
|
{
|
||
|
printf("Current Firmware Versions:\n");
|
||
|
printf(" RGB camera firmware: %d.%d.%d\n",
|
||
|
firmware_version.rgb.major,
|
||
|
firmware_version.rgb.minor,
|
||
|
firmware_version.rgb.iteration);
|
||
|
printf(" Depth camera firmware: %d.%d.%d\n",
|
||
|
firmware_version.depth.major,
|
||
|
firmware_version.depth.minor,
|
||
|
firmware_version.depth.iteration);
|
||
|
printf(" Depth config file: %d.%d\n",
|
||
|
firmware_version.depth_sensor.major,
|
||
|
firmware_version.depth_sensor.minor);
|
||
|
printf(" Audio firmware: %d.%d.%d\n",
|
||
|
firmware_version.audio.major,
|
||
|
firmware_version.audio.minor,
|
||
|
firmware_version.audio.iteration);
|
||
|
|
||
|
log_firmware_build_config((k4a_firmware_build_t)firmware_version.firmware_build);
|
||
|
log_firmware_signature_type((k4a_firmware_signature_t)firmware_version.firmware_signature, true);
|
||
|
}
|
||
|
|
||
|
k4a_result_t open_firmware_device(firmware_t *firmware_handle)
|
||
|
{
|
||
|
int retry = 0;
|
||
|
uint32_t device_count = 0;
|
||
|
|
||
|
while (device_count == 0 && retry++ < 20)
|
||
|
{
|
||
|
K4A_TEST_VERIFY_SUCCEEDED(usb_cmd_get_device_count(&device_count));
|
||
|
|
||
|
if (device_count == 0)
|
||
|
{
|
||
|
ThreadAPI_Sleep(500);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
K4A_TEST_VERIFY_LE(retry, 20);
|
||
|
|
||
|
LOG_INFO("Opening firmware device...", 0);
|
||
|
K4A_TEST_VERIFY_SUCCEEDED(firmware_create(g_serial_number, false, firmware_handle));
|
||
|
K4A_TEST_VERIFY_TRUE(*firmware_handle != nullptr);
|
||
|
|
||
|
return K4A_RESULT_SUCCEEDED;
|
||
|
}
|
||
|
|
||
|
k4a_result_t reset_device(firmware_t *firmware_handle)
|
||
|
{
|
||
|
LOG_INFO("Resetting device...", 0);
|
||
|
K4A_TEST_VERIFY_SUCCEEDED(firmware_reset_device(*firmware_handle));
|
||
|
|
||
|
firmware_destroy(*firmware_handle);
|
||
|
*firmware_handle = nullptr;
|
||
|
|
||
|
ThreadAPI_Sleep(1000);
|
||
|
|
||
|
// Re-open the device to ensure it is ready for use.
|
||
|
return TRACE_CALL(open_firmware_device(firmware_handle));
|
||
|
}
|
||
|
|
||
|
k4a_result_t disconnect_device(firmware_t *firmware_handle)
|
||
|
{
|
||
|
LOG_INFO("Disconnecting device...", 0);
|
||
|
|
||
|
K4A_TEST_VERIFY_SUCCEEDED(g_connection_exerciser->set_usb_port(0));
|
||
|
|
||
|
ThreadAPI_Sleep(1000);
|
||
|
|
||
|
K4A_TEST_VERIFY_SUCCEEDED(g_connection_exerciser->set_usb_port(g_k4a_port_number));
|
||
|
|
||
|
firmware_destroy(*firmware_handle);
|
||
|
*firmware_handle = nullptr;
|
||
|
|
||
|
ThreadAPI_Sleep(1000);
|
||
|
|
||
|
// Re-open the device to ensure it is ready for use.
|
||
|
return TRACE_CALL(open_firmware_device(firmware_handle));
|
||
|
}
|
||
|
|
||
|
k4a_result_t interrupt_operation(firmware_t *firmware_handle, firmware_operation_interruption_t interruption)
|
||
|
{
|
||
|
switch (interruption)
|
||
|
{
|
||
|
case FIRMWARE_INTERRUPTION_RESET:
|
||
|
return TRACE_CALL(reset_device(firmware_handle));
|
||
|
break;
|
||
|
|
||
|
case FIRMWARE_INTERRUPTION_DISCONNECT:
|
||
|
return TRACE_CALL(disconnect_device(firmware_handle));
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
LOG_ERROR("Unknown interruption type");
|
||
|
return K4A_RESULT_FAILED;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
k4a_result_t interrupt_device_at_update_stage(firmware_t *firmware_handle,
|
||
|
firmware_operation_component_t component,
|
||
|
firmware_operation_interruption_t interruption,
|
||
|
firmware_status_summary_t *final_status,
|
||
|
bool verbose_logging)
|
||
|
{
|
||
|
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, firmware_handle == nullptr);
|
||
|
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, final_status == nullptr);
|
||
|
|
||
|
bool all_complete = false;
|
||
|
k4a_result_t result = K4A_RESULT_FAILED;
|
||
|
firmware_status_summary_t previous_status = {};
|
||
|
|
||
|
tickcounter_ms_t start_time_ms, now;
|
||
|
|
||
|
TICK_COUNTER_HANDLE tick = tickcounter_create();
|
||
|
K4A_TEST_VERIFY_TRUE(tick != nullptr);
|
||
|
|
||
|
K4A_TEST_VERIFY_EQUAL(0, tickcounter_get_current_ms(tick, &start_time_ms));
|
||
|
|
||
|
do
|
||
|
{
|
||
|
// This is not necessarily the final status we will get, but at any point could return and the caller needs to
|
||
|
// know the state of the update when we return.
|
||
|
result = firmware_get_download_status(*firmware_handle, final_status);
|
||
|
if (result == K4A_RESULT_SUCCEEDED)
|
||
|
{
|
||
|
if (verbose_logging)
|
||
|
{
|
||
|
if (memcmp(&previous_status.audio, &final_status->audio, sizeof(firmware_component_status_t)) != 0)
|
||
|
{
|
||
|
LOG_INFO("Audio: A:%d V:%d T:%d E:%d W:%d O:%d",
|
||
|
final_status->audio.authentication_check,
|
||
|
final_status->audio.version_check,
|
||
|
final_status->audio.image_transfer,
|
||
|
final_status->audio.flash_erase,
|
||
|
final_status->audio.flash_write,
|
||
|
final_status->audio.overall);
|
||
|
}
|
||
|
|
||
|
if (memcmp(&previous_status.depth_config,
|
||
|
&final_status->depth_config,
|
||
|
sizeof(firmware_component_status_t)) != 0)
|
||
|
{
|
||
|
LOG_INFO("Depth Config: A:%d V:%d T:%d E:%d W:%d O:%d",
|
||
|
final_status->depth_config.authentication_check,
|
||
|
final_status->depth_config.version_check,
|
||
|
final_status->depth_config.image_transfer,
|
||
|
final_status->depth_config.flash_erase,
|
||
|
final_status->depth_config.flash_write,
|
||
|
final_status->depth_config.overall);
|
||
|
}
|
||
|
|
||
|
if (memcmp(&previous_status.depth, &final_status->depth, sizeof(firmware_component_status_t)) != 0)
|
||
|
{
|
||
|
LOG_INFO("Depth: A:%d V:%d T:%d E:%d W:%d O:%d",
|
||
|
final_status->depth.authentication_check,
|
||
|
final_status->depth.version_check,
|
||
|
final_status->depth.image_transfer,
|
||
|
final_status->depth.flash_erase,
|
||
|
final_status->depth.flash_write,
|
||
|
final_status->depth.overall);
|
||
|
}
|
||
|
|
||
|
if (memcmp(&previous_status.rgb, &final_status->rgb, sizeof(firmware_component_status_t)) != 0)
|
||
|
{
|
||
|
LOG_INFO("RGB: A:%d V:%d T:%d E:%d W:%d O:%d",
|
||
|
final_status->rgb.authentication_check,
|
||
|
final_status->rgb.version_check,
|
||
|
final_status->rgb.image_transfer,
|
||
|
final_status->rgb.flash_erase,
|
||
|
final_status->rgb.flash_write,
|
||
|
final_status->rgb.overall);
|
||
|
}
|
||
|
|
||
|
previous_status = *final_status;
|
||
|
}
|
||
|
|
||
|
all_complete = ((final_status->audio.overall > FIRMWARE_OPERATION_INPROGRESS) &&
|
||
|
(final_status->depth_config.overall > FIRMWARE_OPERATION_INPROGRESS) &&
|
||
|
(final_status->depth.overall > FIRMWARE_OPERATION_INPROGRESS) &&
|
||
|
(final_status->rgb.overall > FIRMWARE_OPERATION_INPROGRESS));
|
||
|
|
||
|
// Check to see if now is the correct time to interrupt the device.
|
||
|
switch (component)
|
||
|
{
|
||
|
case FIRMWARE_OPERATION_START:
|
||
|
// As early as possible reset the device.
|
||
|
return TRACE_CALL(interrupt_operation(firmware_handle, interruption));
|
||
|
|
||
|
case FIRMWARE_OPERATION_AUDIO_ERASE:
|
||
|
if (final_status->audio.image_transfer == FIRMWARE_OPERATION_SUCCEEDED)
|
||
|
{
|
||
|
// The erase takes places after the transfer is complete and takes about 7.8 seconds.
|
||
|
int sleepTime = (int)((rand() / (float)RAND_MAX) * 7600);
|
||
|
sleepTime = 3800;
|
||
|
LOG_INFO("Audio Erase started, waiting %dms.", sleepTime);
|
||
|
ThreadAPI_Sleep(sleepTime);
|
||
|
TRACE_CALL(firmware_get_download_status(*firmware_handle, final_status));
|
||
|
return TRACE_CALL(interrupt_operation(firmware_handle, interruption));
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case FIRMWARE_OPERATION_AUDIO_WRITE:
|
||
|
if (final_status->audio.flash_erase == FIRMWARE_OPERATION_SUCCEEDED)
|
||
|
{
|
||
|
// The write takes places after the erase is complete and takes about 20 seconds.
|
||
|
int sleepTime = (int)((rand() / (float)RAND_MAX) * 19700);
|
||
|
sleepTime = 9850;
|
||
|
LOG_INFO("Audio Write started, waiting %dms.", sleepTime);
|
||
|
ThreadAPI_Sleep(sleepTime);
|
||
|
TRACE_CALL(firmware_get_download_status(*firmware_handle, final_status));
|
||
|
return TRACE_CALL(interrupt_operation(firmware_handle, interruption));
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case FIRMWARE_OPERATION_DEPTH_ERASE:
|
||
|
if (final_status->depth.image_transfer == FIRMWARE_OPERATION_SUCCEEDED)
|
||
|
{
|
||
|
// The erase takes places after the transfer is complete and takes about 0.25 seconds.
|
||
|
int sleepTime = (int)((rand() / (float)RAND_MAX) * 100);
|
||
|
sleepTime = 50;
|
||
|
LOG_INFO("Depth Erase started, waiting %dms.", sleepTime);
|
||
|
ThreadAPI_Sleep(sleepTime);
|
||
|
TRACE_CALL(firmware_get_download_status(*firmware_handle, final_status));
|
||
|
return TRACE_CALL(interrupt_operation(firmware_handle, interruption));
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case FIRMWARE_OPERATION_DEPTH_WRITE:
|
||
|
if (final_status->depth.flash_erase == FIRMWARE_OPERATION_SUCCEEDED)
|
||
|
{
|
||
|
// The write takes places after the transfer is complete and takes about 5.8 seconds.
|
||
|
int sleepTime = (int)((rand() / (float)RAND_MAX) * 5700);
|
||
|
sleepTime = 2850;
|
||
|
LOG_INFO("Depth Write started, waiting %dms.", sleepTime);
|
||
|
ThreadAPI_Sleep(sleepTime);
|
||
|
TRACE_CALL(firmware_get_download_status(*firmware_handle, final_status));
|
||
|
return TRACE_CALL(interrupt_operation(firmware_handle, interruption));
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case FIRMWARE_OPERATION_RGB_ERASE:
|
||
|
if (final_status->rgb.image_transfer == FIRMWARE_OPERATION_SUCCEEDED)
|
||
|
{
|
||
|
// The erase takes places after the transfer is complete and takes about 0.05 seconds.
|
||
|
LOG_INFO("RGB erase started...");
|
||
|
TRACE_CALL(firmware_get_download_status(*firmware_handle, final_status));
|
||
|
return TRACE_CALL(interrupt_operation(firmware_handle, interruption));
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case FIRMWARE_OPERATION_RGB_WRITE:
|
||
|
if (final_status->rgb.flash_erase == FIRMWARE_OPERATION_SUCCEEDED)
|
||
|
{
|
||
|
// The write takes places after the transfer is complete and takes about 6.1 seconds.
|
||
|
int sleepTime = (int)((rand() / (float)RAND_MAX) * 6000);
|
||
|
sleepTime = 3000;
|
||
|
LOG_INFO("RGB Write started, waiting %dms.", sleepTime);
|
||
|
ThreadAPI_Sleep(sleepTime);
|
||
|
TRACE_CALL(firmware_get_download_status(*firmware_handle, final_status));
|
||
|
return TRACE_CALL(interrupt_operation(firmware_handle, interruption));
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Failed to get the status of the update operation. Attempt to reset the device and return.
|
||
|
LOG_ERROR("Failed to get the firmware update status.");
|
||
|
TRACE_CALL(interrupt_operation(firmware_handle, interruption));
|
||
|
return K4A_RESULT_FAILED;
|
||
|
}
|
||
|
|
||
|
K4A_TEST_VERIFY_EQUAL(0, tickcounter_get_current_ms(tick, &now));
|
||
|
|
||
|
if (!all_complete && (now - start_time_ms > UPDATE_TIMEOUT_MS))
|
||
|
{
|
||
|
// The update hasn't completed and too much time as passed. Attempt to reset the device and return.
|
||
|
LOG_ERROR("Timeout waiting for the update to complete.");
|
||
|
TRACE_CALL(interrupt_operation(firmware_handle, interruption));
|
||
|
return K4A_RESULT_FAILED;
|
||
|
}
|
||
|
|
||
|
if (!all_complete)
|
||
|
{
|
||
|
ThreadAPI_Sleep(UPDATE_POLL_INTERVAL_MS);
|
||
|
}
|
||
|
} while (!all_complete);
|
||
|
|
||
|
// At this point the update has completed, the device needs to be reset after the
|
||
|
// update has progressed.
|
||
|
return TRACE_CALL(interrupt_operation(firmware_handle, interruption));
|
||
|
}
|
||
|
|
||
|
k4a_result_t perform_device_update(firmware_t *firmware_handle,
|
||
|
uint8_t *firmware_buffer,
|
||
|
size_t firmware_size,
|
||
|
firmware_package_info_t firmware_package_info,
|
||
|
bool verbose_logging)
|
||
|
{
|
||
|
firmware_status_summary_t finalStatus;
|
||
|
k4a_hardware_version_t current_version;
|
||
|
|
||
|
K4A_TEST_VERIFY_SUCCEEDED(firmware_get_device_version(*firmware_handle, ¤t_version));
|
||
|
log_device_version(current_version);
|
||
|
log_firmware_version(firmware_package_info);
|
||
|
|
||
|
// Perform upgrade...
|
||
|
K4A_TEST_VERIFY_SUCCEEDED(firmware_download(*firmware_handle, firmware_buffer, firmware_size));
|
||
|
interrupt_device_at_update_stage(firmware_handle,
|
||
|
FIRMWARE_OPERATION_FULL_DEVICE,
|
||
|
FIRMWARE_INTERRUPTION_RESET,
|
||
|
&finalStatus,
|
||
|
verbose_logging);
|
||
|
|
||
|
K4A_TEST_VERIFY_EQUAL(FIRMWARE_OPERATION_SUCCEEDED, calculate_overall_component_status(finalStatus.audio));
|
||
|
K4A_TEST_VERIFY_EQUAL(FIRMWARE_OPERATION_SUCCEEDED, calculate_overall_component_status(finalStatus.depth_config));
|
||
|
K4A_TEST_VERIFY_EQUAL(FIRMWARE_OPERATION_SUCCEEDED, calculate_overall_component_status(finalStatus.depth));
|
||
|
K4A_TEST_VERIFY_EQUAL(FIRMWARE_OPERATION_SUCCEEDED, calculate_overall_component_status(finalStatus.rgb));
|
||
|
|
||
|
// Check upgrade...
|
||
|
K4A_TEST_VERIFY_SUCCEEDED(firmware_get_device_version(*firmware_handle, ¤t_version));
|
||
|
printf("Post full update ");
|
||
|
log_device_version(current_version);
|
||
|
K4A_TEST_VERIFY_TRUE(compare_version(current_version, firmware_package_info));
|
||
|
|
||
|
return K4A_RESULT_SUCCEEDED;
|
||
|
}
|
||
|
|
||
|
int main(int argc, char **argv)
|
||
|
{
|
||
|
bool error = false;
|
||
|
|
||
|
srand((unsigned int)time(0)); // use current time as seed for random generator
|
||
|
|
||
|
k4a_unittest_init();
|
||
|
::testing::InitGoogleTest(&argc, argv);
|
||
|
|
||
|
for (int i = 1; i < argc; ++i)
|
||
|
{
|
||
|
char *argument = argv[i];
|
||
|
|
||
|
for (int j = 0; argument[j]; j++)
|
||
|
{
|
||
|
argument[j] = (char)tolower(argument[j]);
|
||
|
}
|
||
|
|
||
|
if (strcmp(argument, "-cf") == 0 || strcmp(argument, "--candidate-firmware") == 0)
|
||
|
{
|
||
|
if (i + 1 <= argc)
|
||
|
{
|
||
|
g_candidate_firmware_path = argv[++i];
|
||
|
printf("Setting g_candidate_firmware_path = %s\n", g_candidate_firmware_path);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
printf("Error: candidate firmware path parameter missing\n");
|
||
|
error = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (strcmp(argument, "-tf") == 0 || strcmp(argument, "--test-firmware") == 0)
|
||
|
{
|
||
|
if (i + 1 <= argc)
|
||
|
{
|
||
|
g_test_firmware_path = argv[++i];
|
||
|
printf("Setting g_test_firmware_path = %s\n", g_test_firmware_path);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
printf("Error: test firmware path parameter missing\n");
|
||
|
error = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (strcmp(argument, "-lf") == 0 || strcmp(argument, "--lkg-firmware") == 0)
|
||
|
{
|
||
|
if (i + 1 <= argc)
|
||
|
{
|
||
|
g_lkg_firmware_path = argv[++i];
|
||
|
printf("Setting g_lkg_firmware_path = %s\n", g_lkg_firmware_path);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
printf("Error: lkg firmware path parameter missing\n");
|
||
|
error = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (strcmp(argument, "-ff") == 0 || strcmp(argument, "--factory-firmware") == 0)
|
||
|
{
|
||
|
if (i + 1 <= argc)
|
||
|
{
|
||
|
g_factory_firmware_path = argv[++i];
|
||
|
printf("Setting g_factory_firmware_path = %s\n", g_factory_firmware_path);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
printf("Error: factory firmware path parameter missing\n");
|
||
|
error = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ((strcmp(argument, "-h") == 0) || (strcmp(argument, "/h") == 0) || (strcmp(argument, "-?") == 0) ||
|
||
|
(strcmp(argument, "/?") == 0))
|
||
|
{
|
||
|
error = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (error)
|
||
|
{
|
||
|
printf("\n\nCustom Test Settings:\n");
|
||
|
printf(" -cf | --candidate-firmware <firmware path>\n");
|
||
|
printf(" This is the path to the candidate firmware that should be tested.\n");
|
||
|
printf(" -tf | --test-firmware <firmware path>\n");
|
||
|
printf(" This is the path to the test firmware that will be used to test the candidate. It should have a "
|
||
|
"different version for all components.\n");
|
||
|
printf(" -lf | --lkg-firmware <firmware path>\n");
|
||
|
printf(" This is the path to the LKG firmware that will be used as a starting point for updating to the "
|
||
|
"candidate.\n");
|
||
|
printf(" -ff | --factory-firmware <firmware path>\n");
|
||
|
printf(" This is the path to the factory firmware that will be used as a starting point for updating to "
|
||
|
"the candidate.\n");
|
||
|
|
||
|
return 1; // Indicates an error or warning
|
||
|
}
|
||
|
|
||
|
int result = RUN_ALL_TESTS();
|
||
|
|
||
|
tear_down_common_test();
|
||
|
k4a_unittest_deinit();
|
||
|
return result;
|
||
|
}
|