blob: df4d59422f72731bfccf56053263dd46dae0c3bd [file] [log] [blame]
// Copyright 2019 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 "dev-lid.h"
#include <inttypes.h>
#include <lib/fake_ddk/fake_ddk.h>
#include <lib/mock-hidbus-ifc/mock-hidbus-ifc.h>
#include <zircon/syscalls/port.h>
#include <memory>
#include <acpica/acpi.h>
#include <fbl/auto_call.h>
#include <zxtest/zxtest.h>
namespace {
// Container for mocked ACPI state. Used in conjunction with the CreateFake*Fn
// methods below to mock out ACPI functionality.
struct AcpiState {
UINT64 lid_state = 0;
ACPI_NOTIFY_HANDLER notify_handler = nullptr;
};
acpi_lid::AcpiObjectEvalFunc CreateFakeAcpiEvalFn(AcpiState* state) {
return [state](ACPI_HANDLE handle, ACPI_STRING pathname, ACPI_OBJECT_LIST* external_params,
ACPI_BUFFER* return_buffer, ACPI_OBJECT_TYPE return_type) {
EXPECT_EQ(return_type, ACPI_TYPE_INTEGER, "");
EXPECT_STR_EQ(pathname, "_LID");
static_cast<ACPI_OBJECT*>(return_buffer->Pointer)->Integer.Value = state->lid_state;
return AE_OK;
};
}
acpi_lid::AcpiInstallNotifyHandlerFunc CreateFakeAcpiInstallNotifyHandlerFn(AcpiState* state) {
return [state](ACPI_HANDLE handle, UINT32 handler_type, ACPI_NOTIFY_HANDLER handler, void* ctx) {
EXPECT_EQ(state->notify_handler, nullptr, "Handler already installed");
EXPECT_EQ(handler_type, ACPI_DEVICE_NOTIFY);
state->notify_handler = handler;
return AE_OK;
};
}
acpi_lid::AcpiRemoveNotifyHandlerFunc CreateFakeAcpiRemoveNotifyHandlerFn(AcpiState* state) {
return [state](ACPI_HANDLE handle, UINT32 handler_type, ACPI_NOTIFY_HANDLER handler) {
EXPECT_EQ(handler_type, ACPI_DEVICE_NOTIFY);
EXPECT_EQ(handler, state->notify_handler);
state->notify_handler = nullptr;
return AE_OK;
};
}
} // namespace
namespace acpi_lid {
TEST(LidTest, Init) {
{
AcpiState state;
state.lid_state = 1;
std::unique_ptr<AcpiLidDevice> device;
EXPECT_OK(AcpiLidDevice::Create(
fake_ddk::kFakeParent, nullptr, &device, CreateFakeAcpiEvalFn(&state),
CreateFakeAcpiInstallNotifyHandlerFn(&state), CreateFakeAcpiRemoveNotifyHandlerFn(&state)));
EXPECT_EQ(device->State(), LidState::OPEN);
}
{
AcpiState state;
state.lid_state = 0;
std::unique_ptr<AcpiLidDevice> device;
EXPECT_OK(AcpiLidDevice::Create(
fake_ddk::kFakeParent, nullptr, &device, CreateFakeAcpiEvalFn(&state),
CreateFakeAcpiInstallNotifyHandlerFn(&state), CreateFakeAcpiRemoveNotifyHandlerFn(&state)));
EXPECT_EQ(device->State(), LidState::CLOSED);
}
}
TEST(LidTest, StartStopHid) {
AcpiState state;
state.lid_state = 1;
std::unique_ptr<AcpiLidDevice> device;
EXPECT_OK(AcpiLidDevice::Create(
fake_ddk::kFakeParent, nullptr, &device, CreateFakeAcpiEvalFn(&state),
CreateFakeAcpiInstallNotifyHandlerFn(&state), CreateFakeAcpiRemoveNotifyHandlerFn(&state)));
ASSERT_EQ(device->State(), LidState::OPEN);
mock_hidbus_ifc::MockHidbusIfc<uint8_t> mock_ifc;
EXPECT_OK(device->HidbusStart(mock_ifc.proto()));
EXPECT_NE(state.notify_handler, nullptr);
device->HidbusStop();
EXPECT_EQ(state.notify_handler, nullptr);
}
TEST(LidTest, HidGetDescriptor) {
AcpiState state;
state.lid_state = 0;
std::unique_ptr<AcpiLidDevice> device;
EXPECT_OK(AcpiLidDevice::Create(
fake_ddk::kFakeParent, nullptr, &device, CreateFakeAcpiEvalFn(&state),
CreateFakeAcpiInstallNotifyHandlerFn(&state), CreateFakeAcpiRemoveNotifyHandlerFn(&state)));
uint8_t data[HID_MAX_DESC_LEN];
size_t len;
ASSERT_OK(device->HidbusGetDescriptor(HID_DESCRIPTION_TYPE_REPORT, data, sizeof(data), &len));
ASSERT_EQ(len, AcpiLidDevice::kHidDescriptorLen);
EXPECT_BYTES_EQ(data, AcpiLidDevice::kHidDescriptor, AcpiLidDevice::kHidDescriptorLen);
}
TEST(LidTest, HidGetReport) {
{
AcpiState state;
state.lid_state = 0;
std::unique_ptr<AcpiLidDevice> device;
EXPECT_OK(AcpiLidDevice::Create(
fake_ddk::kFakeParent, nullptr, &device, CreateFakeAcpiEvalFn(&state),
CreateFakeAcpiInstallNotifyHandlerFn(&state), CreateFakeAcpiRemoveNotifyHandlerFn(&state)));
uint8_t report;
size_t copied_len;
device->HidbusGetReport(HID_REPORT_TYPE_INPUT, 0, &report, sizeof(report), &copied_len);
ASSERT_EQ(copied_len, 1U);
EXPECT_EQ(report, 0U);
}
{
AcpiState state;
state.lid_state = 1;
std::unique_ptr<AcpiLidDevice> device;
EXPECT_OK(AcpiLidDevice::Create(
fake_ddk::kFakeParent, nullptr, &device, CreateFakeAcpiEvalFn(&state),
CreateFakeAcpiInstallNotifyHandlerFn(&state), CreateFakeAcpiRemoveNotifyHandlerFn(&state)));
uint8_t report;
size_t copied_len;
device->HidbusGetReport(HID_REPORT_TYPE_INPUT, 0, &report, sizeof(report), &copied_len);
ASSERT_EQ(copied_len, 1U);
EXPECT_EQ(report, 1U);
}
}
TEST(LidTest, UpdateState) {
AcpiState state;
state.lid_state = 1;
std::unique_ptr<AcpiLidDevice> device;
EXPECT_OK(AcpiLidDevice::Create(
fake_ddk::kFakeParent, nullptr, &device, CreateFakeAcpiEvalFn(&state),
CreateFakeAcpiInstallNotifyHandlerFn(&state), CreateFakeAcpiRemoveNotifyHandlerFn(&state)));
ASSERT_EQ(device->State(), LidState::OPEN);
mock_hidbus_ifc::MockHidbusIfc<uint8_t> mock_ifc;
ASSERT_OK(device->HidbusStart(mock_ifc.proto()));
ASSERT_NE(state.notify_handler, nullptr);
// Simulate an ACPI state change notification
state.lid_state = 0;
state.notify_handler(nullptr, 0x80, device.get());
ASSERT_EQ(device->State(), LidState::CLOSED);
ASSERT_OK(mock_ifc.WaitForReports(1));
uint8_t report = *mock_ifc.reports().begin();
ASSERT_EQ(report, 0U);
mock_ifc.Reset();
// Simulate a spurious ACPI state change notification (without an actual
// state change). No HID reports are expected.
state.notify_handler(nullptr, 0x80, device.get());
ASSERT_EQ(device->State(), LidState::CLOSED);
ASSERT_EQ(mock_ifc.PendingReports(), 0U);
// Simulate an ACPI state change notification
state.lid_state = 1;
state.notify_handler(nullptr, 0x80, device.get());
ASSERT_EQ(device->State(), LidState::OPEN);
ASSERT_OK(mock_ifc.WaitForReports(1));
report = *mock_ifc.reports().begin();
ASSERT_EQ(report, 1U);
device->HidbusStop();
}
} // namespace acpi_lid