blob: 031c006f5e23a03fef27b86772a8a0d2baf87196 [file] [log] [blame]
// Copyright 2020 The Fuchsia 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 <fuchsia/device/llcpp/fidl.h>
#include <fuchsia/hardware/mediacodec/llcpp/fidl.h>
#include <lib/fdio/directory.h>
#include <lib/zx/channel.h>
#include <zxtest/zxtest.h>
namespace {
class TestDeviceBase {
public:
TestDeviceBase() {}
void InitializeFromFileName(const char* device_name) {
zx::channel server_endpoint, client_endpoint;
EXPECT_EQ(ZX_OK, zx::channel::create(0, &server_endpoint, &client_endpoint));
EXPECT_EQ(ZX_OK, fdio_service_connect(device_name, server_endpoint.release()));
channel_ = std::move(client_endpoint);
}
// Get a channel to the parent device, so we can rebind the driver to it. This
// can require sandbox access to /dev/sys.
zx::channel GetParentDevice() {
char path[llcpp::fuchsia::device::MAX_DEVICE_PATH_LEN + 1];
auto res = llcpp::fuchsia::device::Controller::Call::GetTopologicalPath(channel());
EXPECT_EQ(ZX_OK, res.status());
EXPECT_TRUE(res->result.is_response());
auto& response = res->result.response();
EXPECT_LE(response.path.size(), llcpp::fuchsia::device::MAX_DEVICE_PATH_LEN);
memcpy(path, response.path.data(), response.path.size());
path[response.path.size()] = 0;
fprintf(stderr, "Device path: %s\n", path);
// Remove everything after the final slash.
*strrchr(path, '/') = 0;
printf("Parent device path: %s\n", path);
zx::channel local_channel, remote_channel;
EXPECT_EQ(ZX_OK, zx::channel::create(0u, &local_channel, &remote_channel));
EXPECT_EQ(ZX_OK, fdio_service_connect(path, remote_channel.release()));
return local_channel;
}
static void UnbindChildren(const zx::channel& parent_device) {
auto res = llcpp::fuchsia::device::Controller::Call::UnbindChildren(
zx::unowned_channel(parent_device));
EXPECT_EQ(ZX_OK, res.status());
EXPECT_TRUE(res->result.is_response());
}
static bool BindDriver(const zx::channel& parent_device, std::string path) {
// Rebinding the device immediately after unbinding it sometimes causes the new device to be
// created before the old one is released, which can cause problems since the old device can
// hold onto interrupts and other resources. Delay recreation to make that less likely.
// TODO(fxb/39852): Remove when the driver framework bug is fixed.
constexpr uint32_t kRecreateDelayMs = 1000;
zx::nanosleep(zx::deadline_after(zx::msec(kRecreateDelayMs)));
constexpr uint32_t kMaxRetryCount = 5;
uint32_t retry_count = 0;
while (retry_count++ < kMaxRetryCount) {
// Don't use rebind because we need the recreate delay above. Also, the parent device may have
// other children that shouldn't be unbound.
auto res = llcpp::fuchsia::device::Controller::Call::Bind(zx::unowned_channel(parent_device),
fidl::unowned_str(path));
EXPECT_EQ(ZX_OK, res.status());
if (res->result.is_err() && res->result.err() == ZX_ERR_ALREADY_BOUND) {
zx::nanosleep(zx::deadline_after(zx::msec(10)));
continue;
}
// This only returns true if the test driver finished binding, which means it successfully ran
// its built-in tests. Else the test driver won't succeed binding.
return res->result.is_response();
}
fprintf(stderr, "Timed out rebinding driver\n");
return false;
}
void Unbind() {
auto res = llcpp::fuchsia::device::Controller::Call::ScheduleUnbind(channel());
fprintf(stderr, "Result: %d\n", res.status());
}
zx::unowned_channel channel() { return zx::unowned_channel(channel_); }
~TestDeviceBase() {}
private:
zx::channel channel_;
};
TEST(TestRunner, RunTests) {
auto test_device = std::make_unique<TestDeviceBase>();
test_device->InitializeFromFileName("/dev/aml-video/amlogic_video");
zx::channel parent_device = test_device->GetParentDevice();
test_device.reset();
TestDeviceBase::UnbindChildren(parent_device);
bool success =
TestDeviceBase::BindDriver(parent_device, "/system/driver/amlogic_video_decoder_test.so");
EXPECT_TRUE(success);
auto test_device2 = std::make_unique<TestDeviceBase>();
test_device2->InitializeFromFileName("/dev/aml-video/test_amlogic_video");
zx::channel local, remote;
ASSERT_OK(zx::channel::create(0, &local, &remote));
ASSERT_OK(fdio_service_connect("/tmp", remote.release()));
auto set_output_res =
llcpp::fuchsia::hardware::mediacodec::Tester::Call::SetOutputDirectoryHandle(
test_device2->channel(), std::move(local));
EXPECT_OK(set_output_res.status());
auto res = llcpp::fuchsia::hardware::mediacodec::Tester::Call::RunTests(test_device2->channel());
EXPECT_OK(res.status());
if (res.ok())
EXPECT_OK(res->result);
// UnbindChildren seems to block for some reason.
test_device2->Unbind();
test_device2.reset();
// Try to rebind the correct driver.
TestDeviceBase::BindDriver(parent_device, "/system/driver/amlogic_video_decoder.so");
}
} // namespace