The AC adapter device communicates AC power state to the guest via ACPI. It supports two modes of operation:
1. QMP control mode (default): AC adapter state is controlled programmatically via QMP commands, ensuring deterministic behavior. 2. Host mirroring mode (optional): The device reflects the host's AC adapter state from sysfs. Probing occurs on guest ACPI requests and at timed intervals. State changes trigger ACPI notifications. Properties: - 'use-qmp': Enable QMP control mode (default: true) - 'enable-sysfs': Enable host AC adapter mirroring (default: false) - 'probe_interval': Probe interval in ms for sysfs mode (default: 2000) - 'sysfs_path': Override default sysfs path /sys/class/power_supply/ The device implements the ACPI_DEV_AML_IF interface to generate its own AML code, placing the ADP0 device directly under \_SB scope. QMP commands: - ac-adapter-set-state: Set AC adapter connection state - query-ac-adapter: Query current AC adapter state Signed-off-by: Leonid Bloch <lb.work...@gmail.com> --- MAINTAINERS | 6 + docs/specs/acad.rst | 195 ++++++++++++ docs/specs/index.rst | 1 + hw/acpi/Kconfig | 4 + hw/acpi/acad.c | 447 +++++++++++++++++++++++++++ hw/acpi/meson.build | 1 + hw/acpi/trace-events | 5 + hw/i386/Kconfig | 1 + include/hw/acpi/acad.h | 27 ++ include/hw/acpi/acpi_dev_interface.h | 1 + qapi/acpi.json | 49 +++ 11 files changed, 737 insertions(+) create mode 100644 docs/specs/acad.rst create mode 100644 hw/acpi/acad.c create mode 100644 include/hw/acpi/acad.h diff --git a/MAINTAINERS b/MAINTAINERS index eb71a4a4b7..19bea634c7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2934,6 +2934,12 @@ S: Maintained F: hw/acpi/battery.* F: docs/specs/battery.rst +AC Adapter +M: Leonid Bloch <lb.work...@gmail.com> +S: Maintained +F: hw/acpi/acad.* +F: docs/specs/acad.rst + Subsystems ---------- Overall Audio backends diff --git a/docs/specs/acad.rst b/docs/specs/acad.rst new file mode 100644 index 0000000000..73d5501b8f --- /dev/null +++ b/docs/specs/acad.rst @@ -0,0 +1,195 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +================= +AC Adapter Device +================= + +The AC adapter device provides AC power state information to the guest. It +supports two operating modes: + +1. **QMP Control Mode** (default): AC adapter state is controlled via QMP + commands, providing deterministic control for testing and migration safety. +2. **Sysfs Mode**: AC adapter state mirrors the host's physical AC adapter, + useful for desktop virtualization where the guest should see the host's + power state. + +Configuration +------------- + +The AC adapter device is created as an ISA device using ``-device acad``. + +Operating Modes +~~~~~~~~~~~~~~~ + +**QMP Control Mode** (``use-qmp=true``, default) + AC adapter state is controlled via QMP commands. This mode is recommended for: + + * Production environments requiring migration support + * Testing with predictable power states + * Environments without host AC adapter access + * Security-sensitive deployments + +**Sysfs Mode** (``enable-sysfs=true``) + AC adapter mirrors the host's physical AC adapter. This mode is useful for: + + * Desktop virtualization on laptops + * Development and testing with real AC adapter behavior + + Note: Sysfs mode reads host files and runs timers, which may impact + security and migration. Use with caution in production. + +Properties +~~~~~~~~~~ + +``ioport`` (default: 0x53c) + I/O port base address for the AC adapter device register. + +``use-qmp`` (default: true) + Enable QMP control mode. When true, AC adapter state is controlled via + QMP commands. Cannot be used together with ``enable-sysfs=true``. + +``enable-sysfs`` (default: false) + Enable sysfs mode to mirror the host's AC adapter. Cannot be used together + with ``use-qmp=true``. + +``probe_interval`` (default: 2000) + Time interval between periodic probes in milliseconds (sysfs mode only). + A zero value disables the periodic probes, and makes the AC adapter state + updates occur on guest requests only. + +``sysfs_path`` (default: auto-detected) + Path to the host's AC adapter sysfs directory (sysfs mode only). By default, + the device auto-detects the first AC adapter of type "Mains" in + ``/sys/class/power_supply/``. Use this property to specify a different + AC adapter, or to provide a custom path for testing purposes. + +Host AC Adapter Detection +------------------------- + +The host's AC adapter information is taken from the sysfs AC adapter +data, located in:: + + /sys/class/power_supply/[device of type "Mains"] + +The device automatically scans for the first AC adapter with: + +- A ``type`` file containing "Mains" +- An ``online`` file that can be read + +If the sysfs path differs, a different AC adapter needs to be probed, +or even if a "fake" host AC adapter is to be provided, the ``sysfs_path`` +property allows overriding the default detection. + +ACPI Interface +-------------- + +The AC adapter device is exposed to the guest as an ACPI device with: + +- **HID**: ``ACPI0003`` (AC Adapter) +- **Device Path**: ``\_SB.ADP0`` +- **Notification Values**: + + - ``0x80``: Status change (connected/disconnected) + +ACPI Methods +~~~~~~~~~~~~ + +``_PSR`` (Power Source) + Returns the current AC adapter state (0 = offline, 1 = online). + +``_PCL`` (Power Consumer List) + Returns the list of devices powered by this adapter. + +``_PIF`` (Power Source Information) + Returns static information about the power source including model number, + serial number, and OEM information. + +I/O Interface +------------- + +The device uses a single I/O port register: + +- **Port**: ``ioport`` property value (default 0x53c) +- **Size**: 1 byte +- **Access**: Read-only + +Register Layout +~~~~~~~~~~~~~~~ + +**PWRS** (offset 0x00, 1 byte) + Current AC adapter state: + + - ``0x00``: AC adapter offline (unplugged) + - ``0x01``: AC adapter online (plugged in) + +QMP Commands +------------ + +When using QMP control mode (default), the following commands are available: + +``ac-adapter-set-state`` + Set the AC adapter connection state. + + * ``connected``: Whether the AC adapter is connected (boolean) + + Example:: + + -> { "execute": "ac-adapter-set-state", + "arguments": { "connected": true }} + <- { "return": {} } + +``query-ac-adapter`` + Query the current AC adapter state. + + Example:: + + -> { "execute": "query-ac-adapter" } + <- { "return": { "connected": true }} + +Examples +-------- + +QMP control mode (default - recommended):: + + # Start with QMP control + qemu-system-x86_64 -device acad -qmp tcp:localhost:4444,server,wait=off + + # From another terminal, set AC adapter state via QMP: + echo '{"execute":"qmp_capabilities"} + {"execute":"ac-adapter-set-state", + "arguments":{"connected":true}}' | \ + nc -N localhost 4444 + +Sysfs mode (mirror host AC adapter):: + + # Enable sysfs mode to mirror host AC adapter + qemu-system-x86_64 -device acad,use-qmp=false,enable-sysfs=true + + # Custom probe interval (5 seconds) + qemu-system-x86_64 -device acad,use-qmp=false,enable-sysfs=true,probe_interval=5000 + + # Specific AC adapter path + qemu-system-x86_64 -device acad,use-qmp=false,enable-sysfs=true,sysfs_path=/sys/class/power_supply/ADP1 + +Testing with fake AC adapter:: + + # Create fake AC adapter files for testing + mkdir -p /tmp/fake_ac + echo "Mains" > /tmp/fake_ac/type + echo "1" > /tmp/fake_ac/online # 1 = connected, 0 = disconnected + + # Use fake AC adapter in sysfs mode + qemu-system-x86_64 -device acad,use-qmp=false,enable-sysfs=true,sysfs_path=/tmp/fake_ac + + # Update AC adapter state while VM is running (from another terminal) + echo "0" > /tmp/fake_ac/online # Disconnect AC adapter + echo "1" > /tmp/fake_ac/online # Reconnect AC adapter + +Combined with battery device:: + + # QMP mode (recommended) + qemu-system-x86_64 -device battery -device acad + + # Sysfs mode (desktop virtualization) + qemu-system-x86_64 -device battery,use-qmp=false,enable-sysfs=true \ + -device acad,use-qmp=false,enable-sysfs=true diff --git a/docs/specs/index.rst b/docs/specs/index.rst index 616e8228cc..e144afcd90 100644 --- a/docs/specs/index.rst +++ b/docs/specs/index.rst @@ -22,6 +22,7 @@ guest hardware that is specific to QEMU. acpi_pci_hotplug acpi_nvdimm acpi_erst + acad battery sev-guest-firmware fw_cfg diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig index 64403378bd..9d28c3addf 100644 --- a/hw/acpi/Kconfig +++ b/hw/acpi/Kconfig @@ -69,6 +69,10 @@ config ACPI_VIOT bool depends on ACPI +config AC_ADAPTER + bool + depends on ACPI + config ACPI_HW_REDUCED bool select ACPI diff --git a/hw/acpi/acad.c b/hw/acpi/acad.c new file mode 100644 index 0000000000..699198c194 --- /dev/null +++ b/hw/acpi/acad.c @@ -0,0 +1,447 @@ +/* + * QEMU emulated AC adapter device. + * + * Copyright (c) 2019 Janus Technologies, Inc. (http://janustech.com) + * + * Authors: + * Leonid Bloch <lb.work...@gmail.com> + * Marcel Apfelbaum <marcel.apfelb...@gmail.com> + * Dmitry Fleytman <dmitry.fleyt...@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + */ + +#include "qemu/osdep.h" +#include "trace.h" +#include "hw/isa/isa.h" +#include "hw/acpi/acpi.h" +#include "hw/nvram/fw_cfg.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" +#include "hw/acpi/acpi_aml_interface.h" +#include "qapi/qapi-commands-acpi.h" + +#include "hw/acpi/acad.h" + +#define AC_ADAPTER_DEVICE(obj) OBJECT_CHECK(ACADState, (obj), \ + TYPE_AC_ADAPTER) + +#define AC_STA_ADDR 0 + +#define SYSFS_PATH "/sys/class/power_supply" +#define AC_ADAPTER_TYPE "Mains" +#define MAX_ALLOWED_TYPE_LENGTH 16 + +enum { + AC_ADAPTER_OFFLINE = 0, + AC_ADAPTER_ONLINE = 1, +}; + +typedef struct ACADState { + ISADevice dev; + MemoryRegion io; + uint16_t ioport; + uint8_t state; + bool use_qmp_control; + bool qmp_connected; + bool enable_sysfs; + + QEMUTimer *probe_state_timer; + uint64_t probe_state_interval; + + char *acad_path; +} ACADState; + +static const char *online_file = "online"; +static const char *type_file = "type"; + +static inline bool acad_file_accessible(char *path, const char *file) +{ + char full_path[PATH_MAX]; + int path_len; + + path_len = snprintf(full_path, PATH_MAX, "%s/%s", path, file); + if (path_len < 0 || path_len >= PATH_MAX) { + return false; + } + + if (access(full_path, R_OK) == 0) { + return true; + } + return false; +} + +static void acad_get_state(ACADState *s) +{ + char file_path[PATH_MAX]; + int path_len; + uint8_t val; + FILE *ff; + + path_len = snprintf(file_path, PATH_MAX, "%s/%s", s->acad_path, + online_file); + if (path_len < 0 || path_len >= PATH_MAX) { + warn_report("Could not read the AC adapter state."); + return; + } + + ff = fopen(file_path, "r"); + if (ff == NULL) { + warn_report("Could not read the AC adapter state."); + return; + } + + if (!fscanf(ff, "%hhu", &val)) { + warn_report("AC adapter state unreadable."); + } else { + switch (val) { + case AC_ADAPTER_OFFLINE: + case AC_ADAPTER_ONLINE: + s->state = val; + break; + default: + warn_report("AC adapter state undetermined."); + } + } + fclose(ff); +} + +static void acad_get_dynamic_status(ACADState *s) +{ + if (s->use_qmp_control) { + s->state = s->qmp_connected ? AC_ADAPTER_ONLINE : AC_ADAPTER_OFFLINE; + } else if (s->enable_sysfs) { + acad_get_state(s); + } else { + s->state = AC_ADAPTER_OFFLINE; + } + + trace_acad_get_dynamic_status(s->state); +} + +static void acad_probe_state(void *opaque) +{ + ACADState *s = opaque; + + uint8_t state_before = s->state; + + acad_get_dynamic_status(s); + + if (state_before != s->state) { + Object *obj = object_resolve_path_type("", TYPE_ACPI_DEVICE_IF, NULL); + acpi_send_event(DEVICE(obj), ACPI_AC_ADAPTER_CHANGE_STATUS); + } + timer_mod(s->probe_state_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + + s->probe_state_interval); +} + +static void acad_probe_state_timer_init(ACADState *s) +{ + if (s->enable_sysfs && s->probe_state_interval > 0) { + s->probe_state_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, + acad_probe_state, s); + timer_mod(s->probe_state_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + + s->probe_state_interval); + } +} + +static bool acad_verify_sysfs(ACADState *s, char *path) +{ + FILE *ff; + char type_path[PATH_MAX]; + int path_len; + char val[MAX_ALLOWED_TYPE_LENGTH]; + + path_len = snprintf(type_path, PATH_MAX, "%s/%s", path, type_file); + if (path_len < 0 || path_len >= PATH_MAX) { + return false; + } + + ff = fopen(type_path, "r"); + if (ff == NULL) { + return false; + } + + if (fgets(val, MAX_ALLOWED_TYPE_LENGTH, ff) == NULL) { + fclose(ff); + return false; + } else { + val[strcspn(val, "\n")] = 0; + if (strncmp(val, AC_ADAPTER_TYPE, MAX_ALLOWED_TYPE_LENGTH)) { + fclose(ff); + return false; + } + } + fclose(ff); + + return acad_file_accessible(path, online_file); +} + +static bool get_acad_path(DeviceState *dev) +{ + ACADState *s = AC_ADAPTER_DEVICE(dev); + DIR *dir; + struct dirent *ent; + char bp[PATH_MAX]; + int path_len; + + if (s->acad_path) { + return acad_verify_sysfs(s, s->acad_path); + } + + dir = opendir(SYSFS_PATH); + if (dir == NULL) { + return false; + } + + ent = readdir(dir); + while (ent != NULL) { + if (ent->d_name[0] != '.') { + path_len = snprintf(bp, PATH_MAX, "%s/%s", SYSFS_PATH, + ent->d_name); + if (path_len < 0 || path_len >= PATH_MAX) { + return false; + } + if (acad_verify_sysfs(s, bp)) { + qdev_prop_set_string(dev, AC_ADAPTER_PATH_PROP, bp); + closedir(dir); + return true; + } + } + ent = readdir(dir); + } + closedir(dir); + + return false; +} + +static void acad_realize(DeviceState *dev, Error **errp) +{ + ISADevice *d = ISA_DEVICE(dev); + ACADState *s = AC_ADAPTER_DEVICE(dev); + FWCfgState *fw_cfg = fw_cfg_find(); + uint16_t *acad_port; + char err_details[32] = {}; + + trace_acad_realize(); + + if (s->use_qmp_control && s->enable_sysfs) { + error_setg(errp, "Cannot enable both QMP control and sysfs mode"); + return; + } + + if (s->enable_sysfs) { + if (!s->acad_path) { + strcpy(err_details, " Try using 'sysfs_path='"); + } + + if (!get_acad_path(dev)) { + error_setg(errp, "AC adapter sysfs path not found or unreadable.%s", + err_details); + return; + } + } + + isa_register_ioport(d, &s->io, s->ioport); + + acad_probe_state_timer_init(s); + + if (!fw_cfg) { + return; + } + + acad_port = g_malloc(sizeof(*acad_port)); + *acad_port = cpu_to_le16(s->ioport); + fw_cfg_add_file(fw_cfg, "etc/acad-port", acad_port, + sizeof(*acad_port)); +} + +static const Property acad_device_properties[] = { + DEFINE_PROP_UINT16(AC_ADAPTER_IOPORT_PROP, ACADState, ioport, 0x53c), + DEFINE_PROP_BOOL("use-qmp", ACADState, use_qmp_control, true), + DEFINE_PROP_BOOL("enable-sysfs", ACADState, enable_sysfs, false), + DEFINE_PROP_UINT64(AC_ADAPTER_PROBE_STATE_INTERVAL, ACADState, + probe_state_interval, 2000), + DEFINE_PROP_STRING(AC_ADAPTER_PATH_PROP, ACADState, acad_path), +}; + +static const VMStateDescription acad_vmstate = { + .name = "acad", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT16(ioport, ACADState), + VMSTATE_UINT64(probe_state_interval, ACADState), + VMSTATE_END_OF_LIST() + } +}; + +static void build_acad_aml(AcpiDevAmlIf *adev, Aml *scope) +{ + Aml *dev, *field, *method, *pkg; + Aml *acad_state; + Aml *sb_scope; + ACADState *s = AC_ADAPTER_DEVICE(adev); + + acad_state = aml_local(0); + + sb_scope = aml_scope("\\_SB"); + dev = aml_device("ADP0"); + aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0003"))); + + aml_append(dev, aml_operation_region("ACST", AML_SYSTEM_IO, + aml_int(s->ioport), + AC_ADAPTER_LEN)); + field = aml_field("ACST", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE); + aml_append(field, aml_named_field("PWRS", 8)); + aml_append(dev, field); + + method = aml_method("_PSR", 0, AML_NOTSERIALIZED); + aml_append(method, aml_store(aml_name("PWRS"), acad_state)); + aml_append(method, aml_return(acad_state)); + aml_append(dev, method); + + method = aml_method("_PCL", 0, AML_NOTSERIALIZED); + pkg = aml_package(1); + aml_append(pkg, aml_name("_SB")); + aml_append(method, aml_return(pkg)); + aml_append(dev, method); + + method = aml_method("_PIF", 0, AML_NOTSERIALIZED); + pkg = aml_package(6); + /* Power Source State */ + aml_append(pkg, aml_int(0)); /* Non-redundant, non-shared */ + /* Maximum Output Power */ + aml_append(pkg, aml_int(AC_ADAPTER_VAL_UNKNOWN)); + /* Maximum Input Power */ + aml_append(pkg, aml_int(AC_ADAPTER_VAL_UNKNOWN)); + /* Model Number */ + aml_append(pkg, aml_string("QADP001")); + /* Serial Number */ + aml_append(pkg, aml_string("SN00000")); + /* OEM Information */ + aml_append(pkg, aml_string("QEMU")); + aml_append(method, aml_return(pkg)); + aml_append(dev, method); + + aml_append(sb_scope, dev); + aml_append(scope, sb_scope); + + /* Status Change */ + method = aml_method("\\_GPE._E0A", 0, AML_NOTSERIALIZED); + aml_append(method, aml_notify(aml_name("\\_SB.ADP0"), aml_int(0x80))); + aml_append(scope, method); +} + +static void acad_class_init(ObjectClass *class, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(class); + AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(class); + + dc->realize = acad_realize; + device_class_set_props(dc, acad_device_properties); + dc->vmsd = &acad_vmstate; + adevc->build_dev_aml = build_acad_aml; +} + +static uint64_t acad_ioport_read(void *opaque, hwaddr addr, unsigned size) +{ + ACADState *s = opaque; + + acad_get_dynamic_status(s); + + switch (addr) { + case AC_STA_ADDR: + return s->state; + default: + warn_report("AC adapter: guest read unknown value."); + trace_acad_ioport_read_unknown(); + return 0; + } +} + +static const MemoryRegionOps acad_ops = { + .read = acad_ioport_read, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, +}; + +static void acad_instance_init(Object *obj) +{ + ACADState *s = AC_ADAPTER_DEVICE(obj); + + memory_region_init_io(&s->io, obj, &acad_ops, s, "acad", + AC_ADAPTER_LEN); +} + +static const TypeInfo acad_info = { + .name = TYPE_AC_ADAPTER, + .parent = TYPE_ISA_DEVICE, + .instance_size = sizeof(ACADState), + .class_init = acad_class_init, + .instance_init = acad_instance_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_ACPI_DEV_AML_IF }, + { }, + }, +}; + +static ACADState *find_acad_device(void) +{ + Object *o = object_resolve_path_type("", TYPE_AC_ADAPTER, NULL); + if (!o) { + return NULL; + } + return AC_ADAPTER_DEVICE(o); +} + +void qmp_ac_adapter_set_state(bool connected, Error **errp) +{ + ACADState *s = find_acad_device(); + + if (!s) { + error_setg(errp, "No AC adapter device found"); + return; + } + + s->qmp_connected = connected; + + Object *obj = object_resolve_path_type("", TYPE_ACPI_DEVICE_IF, NULL); + if (obj) { + acpi_send_event(DEVICE(obj), ACPI_AC_ADAPTER_CHANGE_STATUS); + } +} + +AcAdapterInfo *qmp_query_ac_adapter(Error **errp) +{ + ACADState *s = find_acad_device(); + AcAdapterInfo *ret; + + if (!s) { + error_setg(errp, "No AC adapter device found"); + return NULL; + } + + ret = g_new0(AcAdapterInfo, 1); + + if (s->use_qmp_control) { + ret->connected = s->qmp_connected; + } else { + acad_get_dynamic_status(s); + ret->connected = (s->state == AC_ADAPTER_ONLINE); + } + + return ret; +} + +static void acad_register_types(void) +{ + type_register_static(&acad_info); +} + +type_init(acad_register_types) diff --git a/hw/acpi/meson.build b/hw/acpi/meson.build index 10379a7b2c..2b24951f28 100644 --- a/hw/acpi/meson.build +++ b/hw/acpi/meson.build @@ -32,6 +32,7 @@ if have_tpm acpi_ss.add(files('tpm.c')) endif acpi_ss.add(when: 'CONFIG_BATTERY', if_true: files('battery.c')) +acpi_ss.add(when: 'CONFIG_AC_ADAPTER', if_true: files('acad.c')) system_ss.add(when: 'CONFIG_ACPI', if_false: files('acpi-stub.c', 'aml-build-stub.c', 'ghes-stub.c', 'acpi_interface.c')) system_ss.add(when: 'CONFIG_ACPI_PCI_BRIDGE', if_false: files('pci-bridge-stub.c')) system_ss.add_all(when: 'CONFIG_ACPI', if_true: acpi_ss) diff --git a/hw/acpi/trace-events b/hw/acpi/trace-events index dd3e815482..68ac6e9701 100644 --- a/hw/acpi/trace-events +++ b/hw/acpi/trace-events @@ -92,3 +92,8 @@ acpi_nvdimm_invalid_revision(uint32_t revision) "Revision 0x%" PRIx32 " is not s battery_realize(void) "Battery device realize entry" battery_get_dynamic_status(uint32_t state, uint32_t rate, uint32_t charge) "Battery read state: 0x%"PRIx32", rate: %"PRIu32", charge: %"PRIu32 battery_ioport_read_unknown(void) "Battery read unknown" + +# acad.c +acad_realize(void) "AC adapter device realize entry" +acad_get_dynamic_status(uint8_t state) "AC adapter read state: %"PRIu8 +acad_ioport_read_unknown(void) "AC adapter read unknown" diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index 2c878fd112..baab382a2e 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -40,6 +40,7 @@ config PC imply NVDIMM imply FDC_ISA imply BATTERY + imply AC_ADAPTER select I8259 select I8254 select PCKBD diff --git a/include/hw/acpi/acad.h b/include/hw/acpi/acad.h new file mode 100644 index 0000000000..a4e7149488 --- /dev/null +++ b/include/hw/acpi/acad.h @@ -0,0 +1,27 @@ +/* + * QEMU emulated AC adapter device. + * + * Copyright (c) 2019 Janus Technologies, Inc. (http://janustech.com) + * + * Authors: + * Leonid Bloch <lb.work...@gmail.com> + * Marcel Apfelbaum <marcel.apfelb...@gmail.com> + * Dmitry Fleytman <dmitry.fleyt...@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + */ + +#ifndef HW_ACPI_AC_ADAPTER_H +#define HW_ACPI_AC_ADAPTER_H + +#define TYPE_AC_ADAPTER "acad" +#define AC_ADAPTER_IOPORT_PROP "ioport" +#define AC_ADAPTER_PATH_PROP "sysfs_path" +#define AC_ADAPTER_PROBE_STATE_INTERVAL "probe_interval" + +#define AC_ADAPTER_VAL_UNKNOWN 0xFFFFFFFF + +#define AC_ADAPTER_LEN 1 + +#endif diff --git a/include/hw/acpi/acpi_dev_interface.h b/include/hw/acpi/acpi_dev_interface.h index 3064ef6734..588fbbd05f 100644 --- a/include/hw/acpi/acpi_dev_interface.h +++ b/include/hw/acpi/acpi_dev_interface.h @@ -14,6 +14,7 @@ typedef enum { ACPI_VMGENID_CHANGE_STATUS = 32, ACPI_POWER_DOWN_STATUS = 64, ACPI_BATTERY_CHANGE_STATUS = 128, + ACPI_AC_ADAPTER_CHANGE_STATUS = 1024, } AcpiEventStatusBits; #define TYPE_ACPI_DEVICE_IF "acpi-device-interface" diff --git a/qapi/acpi.json b/qapi/acpi.json index d1ad663bfd..52e151f0e6 100644 --- a/qapi/acpi.json +++ b/qapi/acpi.json @@ -215,3 +215,52 @@ ## { 'command': 'query-battery', 'returns': 'BatteryInfo' } + +## +# @ac-adapter-set-state: +# +# Set the state of the emulated AC adapter device +# +# @connected: whether the AC adapter is connected +# + +# +# Since: 10.2 +# +# .. qmp-example:: +# +# -> { "execute": "ac-adapter-set-state", +# "arguments": { "connected": true } } +# <- { "return": {} } +## +{ 'command': 'ac-adapter-set-state', + 'data': { 'connected': 'bool' } } + +## +# @AcAdapterInfo: +# +# AC adapter state information +# +# @connected: whether the AC adapter is connected +# +# Since: 10.2 +## +{ 'struct': 'AcAdapterInfo', + 'data': { 'connected': 'bool' } } + +## +# @query-ac-adapter: +# +# Query the current state of the emulated AC adapter device +# +# Returns: AC adapter connection state +# +# Since: 10.2 +# +# .. qmp-example:: +# +# -> { "execute": "query-ac-adapter" } +# <- { "return": { "connected": true } } +## +{ 'command': 'query-ac-adapter', + 'returns': 'AcAdapterInfo' } -- 2.51.0