blob: 801f9d5732ba991b2987dfa03f4cfae37410761f [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-battery.h"
#include <dirent.h>
#include <lib/ddk/debug.h>
#include <lib/ddk/device.h>
#include <lib/fit/defer.h>
#include <zircon/syscalls/port.h>
#include <zxtest/zxtest.h>
#define SIGNAL_WAIT_TIMEOUT (5000u)
namespace acpi_battery {
acpi_battery_device_t* dev;
ACPI_STATUS AcpiFakeEvaluateObject(ACPI_HANDLE handle, char* key, ACPI_OBJECT_LIST* args,
ACPI_BUFFER* buffer) {
if (strcmp("_STA", key) == 0) { // device status - e.g battery removed
ACPI_OBJECT* obj = static_cast<ACPI_OBJECT*>(buffer->Pointer);
EXPECT_NOT_NULL(obj);
obj->Integer.Value = 0u;
} else if (strcmp("_BST", key) == 0) { // battery status info
ACPI_OBJECT* pkg = static_cast<ACPI_OBJECT*>(buffer->Pointer);
EXPECT_EQ(pkg->Package.Count, 4);
ACPI_OBJECT* elem = pkg->Package.Elements;
EXPECT_NOT_NULL(elem);
elem[2].Integer.Value = 50;
} else {
return AE_ERROR;
}
return AE_OK;
}
void create_battery_device(void) {
dev = static_cast<acpi_battery_device_t*>(calloc(1, sizeof(acpi_battery_device_t)));
EXPECT_NOT_NULL(dev, "Failed to allocate memory for battery device");
ACPI_HANDLE acpi_handle = NULL;
dev->acpi_handle = acpi_handle;
mtx_init(&dev->lock, mtx_plain);
zx_status_t status = zx_event_create(0, &dev->event);
EXPECT_OK(status);
dev->bst_buffer.Length = ACPI_ALLOCATE_BUFFER;
dev->bst_buffer.Pointer = NULL;
dev->bif_buffer.Length = ACPI_ALLOCATE_BUFFER;
dev->bif_buffer.Pointer = NULL;
dev->power_info.type = POWER_TYPE_BATTERY;
dev->acpi_eval = &AcpiFakeEvaluateObject;
}
void release_battery_device(void) {
if (dev->bst_buffer.Length != ACPI_ALLOCATE_BUFFER) {
ACPI_FREE(dev->bst_buffer.Pointer);
}
if (dev->bif_buffer.Length != ACPI_ALLOCATE_BUFFER) {
ACPI_FREE(dev->bif_buffer.Pointer);
}
if (dev->event != ZX_HANDLE_INVALID) {
zx_handle_close(dev->event);
}
free(dev);
}
void setup(void) { create_battery_device(); }
void teardown(void) { release_battery_device(); }
void verify_battery_change_signal(uint32_t level, uint32_t state) {
setup();
zx_handle_t port;
zx_status_t status = zx_port_create(0, &port);
EXPECT_OK(status);
status = zx_object_wait_async(dev->event, port, 0, ZX_USER_SIGNAL_0, 0);
EXPECT_OK(status);
// fake values to trigger recognition of charging state
ACPI_OBJECT elements[4];
elements[0].Type = ACPI_TYPE_INTEGER;
elements[0].Integer.Value = 0;
elements[1].Type = ACPI_TYPE_INTEGER;
elements[1].Integer.Value = 1;
elements[2].Type = ACPI_TYPE_INTEGER;
elements[2].Integer.Value = level;
elements[3].Type = ACPI_TYPE_INTEGER;
elements[3].Integer.Value = 5;
ACPI_OBJECT pkg;
pkg.Package.Count = 4;
pkg.Package.Elements = elements;
pkg.Type = ACPI_TYPE_PACKAGE;
void* buf = ACPI_ALLOCATE_ZEROED((ACPI_SIZE)(sizeof(pkg)));
const auto cleanup = fit::defer([buf]() { ACPI_FREE(buf); });
dev->bst_buffer.Pointer = &pkg;
// test simulates charge to 50
dev->battery_info.last_full_capacity = 100;
dev->battery_info.remaining_capacity = level;
dev->power_info.state = state;
call_BST(dev);
zx_port_packet_t pkt;
status = zx_port_wait(port, zx_deadline_after(ZX_MSEC(SIGNAL_WAIT_TIMEOUT)), &pkt);
ASSERT_OK(status);
ASSERT_EQ(ZX_PKT_TYPE_SIGNAL_ONE, pkt.type);
teardown();
}
TEST(TestCase, TestSignalOnBatteryChargeLevel) { verify_battery_change_signal(49, 0); }
TEST(TestCase, TestSignalOnBatteryDischargeLevel) { verify_battery_change_signal(51, 0); }
TEST(TestCase, TestSignalOnBatteryChargeState) {
verify_battery_change_signal(50, POWER_STATE_CHARGING);
}
TEST(TestCase, TestSignalOnBatteryDischargeState) {
verify_battery_change_signal(50, POWER_STATE_DISCHARGING);
}
TEST(TestCase, TestSignalOnBatteryDisconnect) {
setup();
zx_handle_t port;
zx_status_t status = zx_port_create(0, &port);
EXPECT_OK(status);
status = zx_object_wait_async(dev->event, port, 0, ZX_USER_SIGNAL_0, 0);
EXPECT_OK(status);
dev->power_info.state = POWER_STATE_ONLINE;
call_STA(dev);
zx_port_packet_t pkt;
status = zx_port_wait(port, zx_deadline_after(ZX_MSEC(SIGNAL_WAIT_TIMEOUT)), &pkt);
ASSERT_OK(status);
ASSERT_EQ(ZX_PKT_TYPE_SIGNAL_ONE, pkt.type);
teardown();
}
} // namespace acpi_battery
// required stubs for faking ddk
zx_driver_rec_t __zircon_driver_rec__ = {};
extern "C" bool driver_log_severity_enabled_internal(const zx_driver_t* drv,
fx_log_severity_t flag) {
return false;
}
extern "C" void driver_logvf_internal(const zx_driver_t* drv, fx_log_severity_t flag,
const char* file, int line, const char* msg, va_list args) {}
extern "C" void driver_logf_internal(const zx_driver_t* drv, fx_log_severity_t flag,
const char* file, int line, const char* msg, ...) {}
const char* device_get_name(zx_device_t* device) { return "fake-acpi-battery"; }
zx_status_t device_add_from_driver(zx_driver_t* drv, zx_device_t* parent, device_add_args_t* args,
zx_device_t** out) {
return ZX_OK;
}