From nobody Tue Nov 18 14:09:00 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1611176198; cv=none; d=zohomail.com; s=zohoarc; b=T1eQzsdcvOF6wwQddTa2G3YGJ/fl0CWk3GUNOeVitfF3VBLqaEezyGfSh7LwyfiCFO5V6f5hIGJNM8BGOZrpJ50leNXEqay5cSmi03UDZRQXxisrsxtukaE0pUAeK4ONF46tFdUoUX6fKWK34umbzczKFFTp4ZLRvOZyra0vow0= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1611176198; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=XmeL9mE/IBaH5W/4gblIhnO7rxtfKGhhnwLIsynrDa0=; b=SGBdqz1MwxoR6POk/N47zVYTld6XDPEJEvvyolrrTFSmtTVkq3Y0QDyS2qLz+aZm0qinB5n1DI2rizl9h0a464UkEgMSsXMhlzSxm3Z78ZNPQsvX/LgbpPVT6Li/4mxUvyMW3QTA9PaAbrJr6ecpSzrhsJEQgm6G7D+6aDh+2ow= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1611176198680828.5273509598653; Wed, 20 Jan 2021 12:56:38 -0800 (PST) Received: from localhost ([::1]:37494 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1l2KWr-0006VC-I0 for importer@patchew.org; Wed, 20 Jan 2021 15:56:37 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:35698) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1l2KVZ-0005CY-E8 for qemu-devel@nongnu.org; Wed, 20 Jan 2021 15:55:17 -0500 Received: from mail-wr1-x430.google.com ([2a00:1450:4864:20::430]:36521) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1l2KVX-0006KJ-DY for qemu-devel@nongnu.org; Wed, 20 Jan 2021 15:55:17 -0500 Received: by mail-wr1-x430.google.com with SMTP id 6so17123053wri.3 for ; Wed, 20 Jan 2021 12:55:15 -0800 (PST) Received: from lb-xps.. ([176.230.215.91]) by smtp.gmail.com with ESMTPSA id k10sm5614823wrq.38.2021.01.20.12.55.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 20 Jan 2021 12:55:13 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=XmeL9mE/IBaH5W/4gblIhnO7rxtfKGhhnwLIsynrDa0=; b=uQ+mSht8PqrJ2RF7WGocG0D5pp32zR5XremNdfgvrfqWEJCudWp1Zalw8uJqzwGn0B UhUnVHuwJ8umcawh5kUYQb2EsIyuhL+9pqzakHVyMUGP+83DCSw/ZGm2edMZs8cPPD1S Xz4se9/jbGXMCRBpbjjZaG0hp2wmozXsF7IH1agsjo9ehtArTT9Bc5ZA/r4/QnSs8YyR Om6g89vn8HOvkRWMv0d7Iz69/pOA5Sp8F61ANiTOrj/XRALNEiozSzNKzQKQNgIBgge/ dBnRTk5DDIc8YKZcMpjZ29R2HEhc2NIFPxs30l8SLEA4TZy1UuqyeUYya46iAlTqvxfz 3rGA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=XmeL9mE/IBaH5W/4gblIhnO7rxtfKGhhnwLIsynrDa0=; b=T2doSlfrYstVQ7Ah1XmviFjOqfowcA9qKWI6jpTm6WoEGeH6Ldk3QMGQO0PK9zRNAm mcKjjppYmL6vM4mzErKHKnC7/N7zSZ/CkugCXBgiOEgTKXXN39Y4Bf0V2/s0AatNpdLV G70FLAGPZtmnlQFaTOdtEPB1wRxjtKbQTCDO2qMAe1rZWSW6bMhWOmT1JRVl6umAoeCa 8xI4OIsrKPddrKFO8jNgB3pMW9RERNTA7/MjTO0H/ThKybIvBBDXfs8/rdzRsFz3LRVf hZfyNark3XKcXnEU7RlE/UfGVZeUn4sOoT2iYSZKuBouSQwZePNuBUEr39WH29kG5No3 SPwg== X-Gm-Message-State: AOAM533KUCWXDOliZlI0x/49JECQp78Zwu9BJG9IymaGTRbuAqnFUA4u zxJ9XnNSRTTCh0y8cuZxnaA= X-Google-Smtp-Source: ABdhPJzdb9i1ZTM+eYRsVCrLu+iagvAHVODZBr75+7qXIlh9/lRwx3V3ZppJ1+ii2tPkws5xpTNFFg== X-Received: by 2002:adf:f403:: with SMTP id g3mr5154264wro.212.1611176114076; Wed, 20 Jan 2021 12:55:14 -0800 (PST) From: Leonid Bloch To: "Michael S . Tsirkin" , Igor Mammedov , Paolo Bonzini , Richard Henderson , Eduardo Habkost , Marcel Apfelbaum Subject: [PATCH 1/4] hw/acpi: Increase the number of possible ACPI interrupts Date: Wed, 20 Jan 2021 22:54:58 +0200 Message-Id: <20210120205501.33918-2-lb.workbox@gmail.com> X-Mailer: git-send-email 2.30.0 In-Reply-To: <20210120205501.33918-1-lb.workbox@gmail.com> References: <20210120205501.33918-1-lb.workbox@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2a00:1450:4864:20::430; envelope-from=lb.workbox@gmail.com; helo=mail-wr1-x430.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Leonid Bloch , qemu-devel@nongnu.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: pass (identity @gmail.com) Content-Type: text/plain; charset="utf-8" Increase the number of possible ACPI interrupts from 8, to the maximum available: 64 by default. Signed-off-by: Leonid Bloch --- hw/acpi/core.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/hw/acpi/core.c b/hw/acpi/core.c index 7170bff657..71ba7c17b8 100644 --- a/hw/acpi/core.c +++ b/hw/acpi/core.c @@ -706,19 +706,32 @@ uint32_t acpi_gpe_ioport_readb(ACPIREGS *ar, uint32_t= addr) void acpi_send_gpe_event(ACPIREGS *ar, qemu_irq irq, AcpiEventStatusBits status) { - ar->gpe.sts[0] |=3D status; + int i; + + AcpiEventStatusBits st =3D status; + for (i =3D 0; i < ar->gpe.len / 2; i++) { + ar->gpe.sts[i] |=3D st; + st >>=3D (sizeof(ar->gpe.sts[0]) * CHAR_BIT); + } + acpi_update_sci(ar, irq); } =20 void acpi_update_sci(ACPIREGS *regs, qemu_irq irq) { int sci_level, pm1a_sts; + uint64_t gpe_sci =3D 0; + int i; =20 pm1a_sts =3D acpi_pm1_evt_get_sts(regs); =20 + for (i =3D 0; i < regs->gpe.len / 2; i++) { + gpe_sci |=3D (regs->gpe.sts[i] & regs->gpe.en[i]); + } + sci_level =3D ((pm1a_sts & regs->pm1.evt.en & ACPI_BITMASK_PM1_COMMON_ENABLED) !=3D= 0) || - ((regs->gpe.sts[0] & regs->gpe.en[0]) !=3D 0); + (gpe_sci !=3D 0); =20 qemu_set_irq(irq, sci_level); =20 --=20 2.30.0 From nobody Tue Nov 18 14:09:00 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1611176322; cv=none; d=zohomail.com; s=zohoarc; b=cnl+9ixsaLamayxFw38zdXI++jBnat2fIEwbzls9DCy9GUGFvoqlFACahK6e0CQHLtaqNTSj/qHlJaaWKCDPG2CvMNoCbo7w6KJ0sxcrLWcMvEoIamejb+nX8Ar0y6JOLrEn2WGuml4UulKRAtupPRi0PiyeTu3OECOK1RZJo0w= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1611176322; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=5GlAb/7pN+l6zznLtiaTMif84j5i4f7lRmC7oIg/6fU=; b=fGKQtpFlsoB4HV9v2PVXtqXr1hErvmDpV/aeJns9Uy8FQRdN8sLBhOCOPBWGbg5Vcmimr9VHT2AgbkO61scTBE46W27nRGJHPE0lKdE09aWlUMiQsX1i8fRoQ01rv+Ecbxs2MPjx8QjpfkBvdVEe7UxasY29qtrvmxKGbbnO1Yc= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1611176322813405.34499778296174; Wed, 20 Jan 2021 12:58:42 -0800 (PST) Received: from localhost ([::1]:42714 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1l2KYr-0000Bz-NT for importer@patchew.org; Wed, 20 Jan 2021 15:58:41 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:35712) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1l2KVc-0005Gq-RR for qemu-devel@nongnu.org; Wed, 20 Jan 2021 15:55:20 -0500 Received: from mail-wm1-x32f.google.com ([2a00:1450:4864:20::32f]:55629) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1l2KVZ-0006KT-KF for qemu-devel@nongnu.org; Wed, 20 Jan 2021 15:55:20 -0500 Received: by mail-wm1-x32f.google.com with SMTP id c124so3940559wma.5 for ; Wed, 20 Jan 2021 12:55:17 -0800 (PST) Received: from lb-xps.. ([176.230.215.91]) by smtp.gmail.com with ESMTPSA id k10sm5614823wrq.38.2021.01.20.12.55.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 20 Jan 2021 12:55:15 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=5GlAb/7pN+l6zznLtiaTMif84j5i4f7lRmC7oIg/6fU=; b=jAk+BHBwQx0JjaSDVexKfrShWLyOpx8oUZfpBO66eC6/M7OnfqxlDMZLGm9+auYfNS 7A1fqr5Sgpbpa7whrqQQJAS/ExsUJ/NclzMjwB7/oKrH8VTFAK4uGalDVqvs2t2ekE83 aOwUi9OYXvRfOkuAxWQSHZwegwrTS+HftAN6dw+8pzPoh/W8Knv7TlOE0o0wB9Pkt10j AkJID83Eu0EjpdUtBRaJhVoHfciYYxaT8Ns9CMoajugsB0EtURHI8VxIbGNVXZ5OCqOV l8yk7QIv1ZYpWpu+Ti85DWollwfoB6bVlhoSXj6WyuB/cu3sk3pIo00vlQycRB3+UHx1 gG9Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=5GlAb/7pN+l6zznLtiaTMif84j5i4f7lRmC7oIg/6fU=; b=Q+WAFee1c0ysZAvoyHPLp18Rgm9z1snORW7k2dXcFDJ0B3dYEN+qwNStKIZ/fzIUUK IZ07XRAFhg0ztJigv1rbI1UxiYpeSiplye8FuwM1r8gD77BXxKAkJ0mdEOu1SkRGnJQM kpSfcbZcKbCqQSUONwPfdatEs8uNB/poM/xystlHjq6XRCcIiUAVsiq16WU5zRzvbJZ4 sOwMthf1AIWNtb1lEqb8wmdgEIdQbTEHtgsHEPd2bPKomdHygOZVFOdWf1IX2dZbF1j/ YjuVCBpGwo2cHzqXiKTE2Owjo3GZqx5JnDKQYHFrTIsByf2A4RGQ47CQBxdRZjnNC1B7 fiTg== X-Gm-Message-State: AOAM531txbBr1+PXfyAjVZ5pcKvpMAqNKUJScQu17jWozGU1g8bPAfca KP3KIeywAoZUOKdeO+ETZuQ= X-Google-Smtp-Source: ABdhPJy8yV2Xd1v7j7F42KJhjT502lU70TO3ZvPknFmD69ceu15g59jFcIejvM28fkQ600g8On5V3g== X-Received: by 2002:a1c:149:: with SMTP id 70mr5997843wmb.165.1611176116112; Wed, 20 Jan 2021 12:55:16 -0800 (PST) From: Leonid Bloch To: "Michael S . Tsirkin" , Igor Mammedov , Paolo Bonzini , Richard Henderson , Eduardo Habkost , Marcel Apfelbaum Subject: [PATCH 2/4] hw/acpi: Introduce the QEMU Battery Date: Wed, 20 Jan 2021 22:54:59 +0200 Message-Id: <20210120205501.33918-3-lb.workbox@gmail.com> X-Mailer: git-send-email 2.30.0 In-Reply-To: <20210120205501.33918-1-lb.workbox@gmail.com> References: <20210120205501.33918-1-lb.workbox@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2a00:1450:4864:20::32f; envelope-from=lb.workbox@gmail.com; helo=mail-wm1-x32f.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Leonid Bloch , qemu-devel@nongnu.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: pass (identity @gmail.com) Content-Type: text/plain; charset="utf-8" The battery device communicates the host's battery state to the guest. The probing of the host's battery state occurs on guest ACPI requests, as well as on timed intervals. If a change of the host's battery state is detected on one of the timed probes (charging/discharging, rate, charge) an ACPI notification is sent to the guest, so it will be able to update its battery status accordingly. The time interval between periodic probes is 2 seconds by default. A property 'probe_interval' allows to modify this value. The value should be provided in milliseconds. A zero value disables the periodic probes, and makes the battery state updates occur on guest requests only. The host's battery information is taken from the sysfs battery data, located in: /sys/class/power_supply/[device of type "Battery"] If the sysfs path differs, a different battery needs to be probed, or even if a "fake" host battery is to be provided, a 'sysfs_path' property allows to override the default one. Signed-off-by: Leonid Bloch Signed-off-by: Marcel Apfelbaum --- MAINTAINERS | 5 + docs/specs/battery.txt | 23 ++ hw/acpi/Kconfig | 4 + hw/acpi/battery.c | 512 +++++++++++++++++++++++++++ hw/acpi/meson.build | 1 + hw/acpi/trace-events | 5 + hw/i386/Kconfig | 1 + hw/i386/acpi-build.c | 97 +++++ include/hw/acpi/acpi_dev_interface.h | 1 + include/hw/acpi/battery.h | 43 +++ 10 files changed, 692 insertions(+) create mode 100644 docs/specs/battery.txt create mode 100644 hw/acpi/battery.c create mode 100644 include/hw/acpi/battery.h diff --git a/MAINTAINERS b/MAINTAINERS index 3216387521..33eea28c22 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2141,6 +2141,11 @@ F: net/can/* F: hw/net/can/* F: include/net/can_*.h =20 +Battery +M: Leonid Bloch +S: Maintained +F: hw/acpi/battery.* + Subsystems ---------- Audio diff --git a/docs/specs/battery.txt b/docs/specs/battery.txt new file mode 100644 index 0000000000..e90324ac03 --- /dev/null +++ b/docs/specs/battery.txt @@ -0,0 +1,23 @@ +BATTERY DEVICE +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +The battery device communicates the host's battery state to the guest. +The probing of the host's battery state occurs on guest ACPI requests, +as well as on timed intervals. If a change of the host's battery state is +detected on one of the timed probes (charging/discharging, rate, charge) +an ACPI notification is sent to the guest, so it will be able to +update its battery status accordingly. + +The time interval between periodic probes is 2 s by default. A property +'probe_interval' allows to modify this value. The value should be +provided in milliseconds. A zero value disables the periodic probes, +and makes the battery state updates occur on guest requests only. + +The host's battery information is taken from the sysfs battery data, +located in: + +/sys/class/power_supply/[device of type "Battery"] + +If the sysfs path differs, a different battery needs to be probed, +or even if a "fake" host battery is to be provided, a 'sysfs_path' +property allows to override the default one. diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig index 1932f66af8..6b4c41037a 100644 --- a/hw/acpi/Kconfig +++ b/hw/acpi/Kconfig @@ -41,4 +41,8 @@ config ACPI_VMGENID default y depends on PC =20 +config BATTERY + bool + depends on ACPI + config ACPI_HW_REDUCED diff --git a/hw/acpi/battery.c b/hw/acpi/battery.c new file mode 100644 index 0000000000..afd82594b1 --- /dev/null +++ b/hw/acpi/battery.c @@ -0,0 +1,512 @@ +/* + * QEMU emulated battery device. + * + * Copyright (c) 2019 Janus Technologies, Inc. (http://janustech.com) + * + * Authors: + * Leonid Bloch + * Marcel Apfelbaum + * Dmitry Fleytman + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory for details. + * + */ + +#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/battery.h" + +#define BATTERY_DEVICE(obj) OBJECT_CHECK(BatteryState, (obj), TYPE_BATTERY) + +#define BATTERY_DISCHARGING 1 +#define BATTERY_CHARGING 2 + +#define SYSFS_PATH "/sys/class/power_supply" +#define BATTERY_TYPE "Battery" + +#define MAX_ALLOWED_STATE_LENGTH 32 /* For convinience when comparing */ + +#define NORMALIZE_BY_FULL(val, full) \ + ((full =3D=3D 0) ? BATTERY_VAL_UNKNOWN \ + : (uint32_t)(val * BATTERY_FULL_CAP / full)) + +typedef union bat_metric { + uint32_t val; + uint8_t acc[4]; +} bat_metric; + +typedef struct BatteryState { + ISADevice dev; + MemoryRegion io; + uint16_t ioport; + bat_metric state; + bat_metric rate; + bat_metric charge; + uint32_t charge_full; + int units; /* 0 - mWh, 1 - mAh */ + + QEMUTimer *probe_state_timer; + uint64_t probe_state_interval; + + char *bat_path; +} BatteryState; + +/* Access addresses */ +enum acc_addr { + bsta_addr0, bsta_addr1, bsta_addr2, bsta_addr3, + brte_addr0, brte_addr1, brte_addr2, brte_addr3, + bcrg_addr0, bcrg_addr1, bcrg_addr2, bcrg_addr3 +}; + +/* Files used when the units are: mWh mAh */ +static const char *full_file[] =3D { "energy_full", "charge_full" }; +static const char *now_file[] =3D { "energy_now", "charge_now" }; +static const char *rate_file[] =3D { "power_now", "current_now" }; + +static const char *stat_file =3D "status"; +static const char *type_file =3D "type"; + +static const char *discharging_states[] =3D { "Discharging", "Not charging= " }; +static const char *charging_states[] =3D { "Charging", "Full", "Unknown" }; + +static inline bool battery_file_accessible(char *path, const char *file) +{ + char full_path[PATH_MAX]; + int path_len; + + path_len =3D snprintf(full_path, PATH_MAX, "%s/%s", path, file); + if (path_len < 0 || path_len >=3D PATH_MAX) { + return false; + } + if (access(full_path, R_OK) =3D=3D 0) { + return true; + } + return false; +} + +static inline int battery_select_file(char *path, const char **file) +{ + if (battery_file_accessible(path, file[0])) { + return 0; + } else if (battery_file_accessible(path, file[1])) { + return 1; + } else { + return -1; + } +} + +static void battery_get_full_charge(BatteryState *s, Error **errp) +{ + char file_path[PATH_MAX]; + int path_len; + uint32_t val; + FILE *ff; + + path_len =3D snprintf(file_path, PATH_MAX, "%s/%s", s->bat_path, + full_file[s->units]); + if (path_len < 0 || path_len >=3D PATH_MAX) { + error_setg(errp, "Full capacity file path is inaccessible."); + return; + } + + ff =3D fopen(file_path, "r"); + if (ff =3D=3D NULL) { + error_setg_errno(errp, errno, "Could not read the full charge file= ."); + return; + } + + if (fscanf(ff, "%u", &val) !=3D 1) { + error_setg(errp, "Full capacity undetermined."); + return; + } else { + s->charge_full =3D val; + } + fclose(ff); +} + +static inline bool battery_is_discharging(char *val) +{ + static const int discharging_len =3D ARRAY_SIZE(discharging_states); + int i; + + for (i =3D 0; i < discharging_len; i++) { + if (!strncmp(val, discharging_states[i], MAX_ALLOWED_STATE_LENGTH)= ) { + return true; + } + } + return false; +} + +static inline bool battery_is_charging(char *val) +{ + static const int charging_len =3D ARRAY_SIZE(charging_states); + int i; + + for (i =3D 0; i < charging_len; i++) { + if (!strncmp(val, charging_states[i], MAX_ALLOWED_STATE_LENGTH)) { + return true; + } + } + return false; +} + +static void battery_get_state(BatteryState *s) +{ + char file_path[PATH_MAX]; + int path_len; + char val[MAX_ALLOWED_STATE_LENGTH]; + FILE *ff; + + path_len =3D snprintf(file_path, PATH_MAX, "%s/%s", s->bat_path, stat_= file); + if (path_len < 0 || path_len >=3D PATH_MAX) { + warn_report("Could not read the battery state."); + return; + } + + ff =3D fopen(file_path, "r"); + if (ff =3D=3D NULL) { + warn_report("Could not read the battery state."); + return; + } + + if (fgets(val, MAX_ALLOWED_STATE_LENGTH, ff) =3D=3D NULL) { + warn_report("Battery state unreadable."); + } else { + val[strcspn(val, "\n")] =3D 0; + if (battery_is_discharging(val)) { + s->state.val =3D BATTERY_DISCHARGING; + } else if (battery_is_charging(val)) { + s->state.val =3D BATTERY_CHARGING; + } else { + warn_report("Battery state undetermined."); + } + } + fclose(ff); +} + +static void battery_get_rate(BatteryState *s) +{ + char file_path[PATH_MAX]; + int path_len; + uint64_t val; + FILE *ff; + + path_len =3D snprintf(file_path, PATH_MAX, "%s/%s", s->bat_path, + rate_file[s->units]); + if (path_len < 0 || path_len >=3D PATH_MAX) { + warn_report("Could not read the battery rate."); + s->rate.val =3D BATTERY_VAL_UNKNOWN; + return; + } + + ff =3D fopen(file_path, "r"); + if (ff =3D=3D NULL) { + warn_report("Could not read the battery rate."); + s->rate.val =3D BATTERY_VAL_UNKNOWN; + return; + } + + if (fscanf(ff, "%lu", &val) !=3D 1) { + warn_report("Battery rate undetermined."); + s->rate.val =3D BATTERY_VAL_UNKNOWN; + } else { + s->rate.val =3D NORMALIZE_BY_FULL(val, s->charge_full); + } + fclose(ff); +} + +static void battery_get_charge(BatteryState *s) +{ + char file_path[PATH_MAX]; + int path_len; + uint64_t val; + FILE *ff; + + path_len =3D snprintf(file_path, PATH_MAX, "%s/%s", s->bat_path, + now_file[s->units]); + if (path_len < 0 || path_len >=3D PATH_MAX) { + warn_report("Could not read the battery charge."); + s->charge.val =3D BATTERY_VAL_UNKNOWN; + return; + } + + ff =3D fopen(file_path, "r"); + if (ff =3D=3D NULL) { + warn_report("Could not read the battery charge."); + s->charge.val =3D BATTERY_VAL_UNKNOWN; + return; + } + + if (fscanf(ff, "%lu", &val) !=3D 1) { + warn_report("Battery charge undetermined."); + s->charge.val =3D BATTERY_VAL_UNKNOWN; + } else { + s->charge.val =3D NORMALIZE_BY_FULL(val, s->charge_full); + } + fclose(ff); +} + +static void battery_get_dynamic_status(BatteryState *s) +{ + battery_get_state(s); + battery_get_rate(s); + battery_get_charge(s); + + trace_battery_get_dynamic_status(s->state.val, s->rate.val, s->charge.= val); +} + +static void battery_probe_state(void *opaque) +{ + BatteryState *s =3D opaque; + + uint32_t state_before =3D s->state.val; + uint32_t rate_before =3D s->rate.val; + uint32_t charge_before =3D s->charge.val; + + battery_get_dynamic_status(s); + + if (state_before !=3D s->state.val || rate_before !=3D s->rate.val || + charge_before !=3D s->charge.val) { + Object *obj =3D object_resolve_path_type("", TYPE_ACPI_DEVICE_IF, = NULL); + switch (charge_before) { + case 0: + break; /* Avoid marking initiation as an update */ + default: + acpi_send_event(DEVICE(obj), ACPI_BATTERY_CHANGE_STATUS); + } + } + timer_mod(s->probe_state_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + + s->probe_state_interval); +} + +static void battery_probe_state_timer_init(BatteryState *s) +{ + if (s->probe_state_interval > 0) { + s->probe_state_timer =3D timer_new_ms(QEMU_CLOCK_VIRTUAL, + battery_probe_state, s); + timer_mod(s->probe_state_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTU= AL) + + s->probe_state_interval); + } +} + +static bool battery_verify_sysfs(BatteryState *s, char *path) +{ + int units; + FILE *ff; + char type_path[PATH_MAX]; + int path_len; + char val[MAX_ALLOWED_STATE_LENGTH]; + + path_len =3D snprintf(type_path, PATH_MAX, "%s/%s", path, type_file); + if (path_len < 0 || path_len >=3D PATH_MAX) { + return false; + } + ff =3D fopen(type_path, "r"); + if (ff =3D=3D NULL) { + return false; + } + + if (fgets(val, MAX_ALLOWED_STATE_LENGTH, ff) =3D=3D NULL) { + fclose(ff); + return false; + } else { + val[strcspn(val, "\n")] =3D 0; + if (strncmp(val, BATTERY_TYPE, MAX_ALLOWED_STATE_LENGTH)) { + fclose(ff); + return false; + } + } + fclose(ff); + + units =3D battery_select_file(path, full_file); + + if (units < 0) { + return false; + } else { + s->units =3D units; + } + + return (battery_file_accessible(path, now_file[s->units]) + & battery_file_accessible(path, rate_file[s->units]) + & battery_file_accessible(path, stat_file)); +} + +static bool get_battery_path(DeviceState *dev) +{ + BatteryState *s =3D BATTERY_DEVICE(dev); + DIR *dir; + struct dirent *ent; + char bp[PATH_MAX]; + int path_len; + + if (s->bat_path) { + return battery_verify_sysfs(s, s->bat_path); + } + + dir =3D opendir(SYSFS_PATH); + if (dir =3D=3D NULL) { + return false; + } + + ent =3D readdir(dir); + while (ent !=3D NULL) { + if (ent->d_name[0] !=3D '.') { + path_len =3D snprintf(bp, PATH_MAX, "%s/%s", SYSFS_PATH, + ent->d_name); + if (path_len < 0 || path_len >=3D PATH_MAX) { + return false; + } + if (battery_verify_sysfs(s, bp)) { + qdev_prop_set_string(dev, BATTERY_PATH_PROP, bp); + closedir(dir); + return true; + } + } + ent =3D readdir(dir); + } + closedir(dir); + + return false; +} + +static void battery_realize(DeviceState *dev, Error **errp) +{ + ISADevice *d =3D ISA_DEVICE(dev); + BatteryState *s =3D BATTERY_DEVICE(dev); + FWCfgState *fw_cfg =3D fw_cfg_find(); + uint16_t *battery_port; + char err_details[0x20] =3D {}; + + trace_battery_realize(); + + if (!s->bat_path) { + strcpy(err_details, " Try using 'sysfs_path=3D'"); + } + + if (!get_battery_path(dev)) { + error_setg(errp, "Battery sysfs path not found or unreadable.%s", + err_details); + return; + } + + battery_get_full_charge(s, errp); + + isa_register_ioport(d, &s->io, s->ioport); + + battery_probe_state_timer_init(s); + + if (!fw_cfg) { + return; + } + + battery_port =3D g_malloc(sizeof(*battery_port)); + *battery_port =3D cpu_to_le16(s->ioport); + fw_cfg_add_file(fw_cfg, "etc/battery-port", battery_port, + sizeof(*battery_port)); +} + +static Property battery_device_properties[] =3D { + DEFINE_PROP_UINT16(BATTERY_IOPORT_PROP, BatteryState, ioport, 0x530), + DEFINE_PROP_UINT64(BATTERY_PROBE_STATE_INTERVAL, BatteryState, + probe_state_interval, 2000), + DEFINE_PROP_STRING(BATTERY_PATH_PROP, BatteryState, bat_path), + DEFINE_PROP_END_OF_LIST(), +}; + +static const VMStateDescription battery_vmstate =3D { + .name =3D "battery", + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (VMStateField[]) { + VMSTATE_UINT16(ioport, BatteryState), + VMSTATE_UINT64(probe_state_interval, BatteryState), + VMSTATE_END_OF_LIST() + } +}; + +static void battery_class_init(ObjectClass *class, void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(class); + + dc->realize =3D battery_realize; + device_class_set_props(dc, battery_device_properties); + dc->vmsd =3D &battery_vmstate; +} + +static uint64_t battery_ioport_read(void *opaque, hwaddr addr, unsigned si= ze) +{ + BatteryState *s =3D opaque; + + battery_get_dynamic_status(s); + + switch (addr) { + case bsta_addr0: + return s->state.acc[0]; + case bsta_addr1: + return s->state.acc[1]; + case bsta_addr2: + return s->state.acc[2]; + case bsta_addr3: + return s->state.acc[3]; + case brte_addr0: + return s->rate.acc[0]; + case brte_addr1: + return s->rate.acc[1]; + case brte_addr2: + return s->rate.acc[2]; + case brte_addr3: + return s->rate.acc[3]; + case bcrg_addr0: + return s->charge.acc[0]; + case bcrg_addr1: + return s->charge.acc[1]; + case bcrg_addr2: + return s->charge.acc[2]; + case bcrg_addr3: + return s->charge.acc[3]; + default: + warn_report("Battery: guest read unknown value."); + trace_battery_ioport_read_unknown(); + return 0; + } +} + +static const MemoryRegionOps battery_ops =3D { + .read =3D battery_ioport_read, + .impl =3D { + .min_access_size =3D 1, + .max_access_size =3D 1, + }, +}; + +static void battery_instance_init(Object *obj) +{ + BatteryState *s =3D BATTERY_DEVICE(obj); + + memory_region_init_io(&s->io, obj, &battery_ops, s, "battery", + BATTERY_LEN); +} + +static const TypeInfo battery_info =3D { + .name =3D TYPE_BATTERY, + .parent =3D TYPE_ISA_DEVICE, + .instance_size =3D sizeof(BatteryState), + .class_init =3D battery_class_init, + .instance_init =3D battery_instance_init, +}; + +static void battery_register_types(void) +{ + type_register_static(&battery_info); +} + +type_init(battery_register_types) diff --git a/hw/acpi/meson.build b/hw/acpi/meson.build index dd69577212..485eab33b6 100644 --- a/hw/acpi/meson.build +++ b/hw/acpi/meson.build @@ -19,6 +19,7 @@ acpi_ss.add(when: 'CONFIG_ACPI_X86_ICH', if_true: files('= ich9.c', 'tco.c')) acpi_ss.add(when: 'CONFIG_IPMI', if_true: files('ipmi.c'), if_false: files= ('ipmi-stub.c')) acpi_ss.add(when: 'CONFIG_PC', if_false: files('acpi-x86-stub.c')) acpi_ss.add(when: 'CONFIG_TPM', if_true: files('tpm.c')) +acpi_ss.add(when: 'CONFIG_BATTERY', if_true: files('battery.c')) softmmu_ss.add(when: 'CONFIG_ACPI', if_false: files('acpi-stub.c', 'aml-bu= ild-stub.c')) softmmu_ss.add_all(when: 'CONFIG_ACPI', if_true: acpi_ss) softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('acpi-stub.c', 'aml-buil= d-stub.c', diff --git a/hw/acpi/trace-events b/hw/acpi/trace-events index f91ced477d..5d2c606496 100644 --- a/hw/acpi/trace-events +++ b/hw/acpi/trace-events @@ -53,3 +53,8 @@ piix4_gpe_writeb(uint64_t addr, unsigned width, uint64_t = val) "addr: 0x%" PRIx64 # tco.c tco_timer_reload(int ticks, int msec) "ticks=3D%d (%d ms)" tco_timer_expired(int timeouts_no, bool strap, bool no_reboot) "timeouts_n= o=3D%d no_reboot=3D%d/%d" + +# battery.c +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" diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index eea059ffef..b081be7a0f 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -23,6 +23,7 @@ config PC imply TPM_TIS_ISA imply VGA_PCI imply VIRTIO_VGA + imply BATTERY select FDC select I8259 select I8254 diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index f56d699c7f..ecd5380f82 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -35,6 +35,7 @@ #include "hw/acpi/acpi-defs.h" #include "hw/acpi/acpi.h" #include "hw/acpi/cpu.h" +#include "hw/acpi/battery.h" #include "hw/nvram/fw_cfg.h" #include "hw/acpi/bios-linker-loader.h" #include "hw/isa/isa.h" @@ -112,6 +113,7 @@ typedef struct AcpiMiscInfo { const unsigned char *dsdt_code; unsigned dsdt_size; uint16_t pvpanic_port; + uint16_t battery_port; uint16_t applesmc_io_base; } AcpiMiscInfo; =20 @@ -277,6 +279,7 @@ static void acpi_get_misc_info(AcpiMiscInfo *info) info->has_hpet =3D hpet_find(); info->tpm_version =3D tpm_get_version(tpm_find()); info->pvpanic_port =3D pvpanic_port(); + info->battery_port =3D battery_port(); info->applesmc_io_base =3D applesmc_port(); } =20 @@ -1631,6 +1634,100 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, aml_append(sb_scope, dev); } =20 + if (misc->battery_port) { + Aml *bat_state =3D aml_local(0); + Aml *bat_rate =3D aml_local(1); + Aml *bat_charge =3D aml_local(2); + + dev =3D aml_device("BAT0"); + aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0C0A"))); + + method =3D aml_method("_STA", 0, AML_NOTSERIALIZED); + aml_append(method, aml_return(aml_int(0x1F))); + aml_append(dev, method); + + aml_append(dev, aml_operation_region("DBST", AML_SYSTEM_IO, + aml_int(misc->battery_port), + BATTERY_LEN)); + field =3D aml_field("DBST", AML_DWORD_ACC, AML_NOLOCK, AML_PRESERV= E); + aml_append(field, aml_named_field("BSTA", 32)); + aml_append(field, aml_named_field("BRTE", 32)); + aml_append(field, aml_named_field("BCRG", 32)); + aml_append(dev, field); + + method =3D aml_method("_BIF", 0, AML_NOTSERIALIZED); + pkg =3D aml_package(13); + /* Power Unit */ + aml_append(pkg, aml_int(0)); /* mW */ + /* Design Capacity */ + aml_append(pkg, aml_int(BATTERY_FULL_CAP)); + /* Last Full Charge Capacity */ + aml_append(pkg, aml_int(BATTERY_FULL_CAP)); + /* Battery Technology */ + aml_append(pkg, aml_int(1)); /* Secondary */ + /* Design Voltage */ + aml_append(pkg, aml_int(BATTERY_VAL_UNKNOWN)); + /* Design Capacity of Warning */ + aml_append(pkg, aml_int(BATTERY_CAPACITY_OF_WARNING)); + /* Design Capacity of Low */ + aml_append(pkg, aml_int(BATTERY_CAPACITY_OF_LOW)); + /* Battery Capacity Granularity 1 */ + aml_append(pkg, aml_int(BATTERY_CAPACITY_GRANULARITY)); + /* Battery Capacity Granularity 2 */ + aml_append(pkg, aml_int(BATTERY_CAPACITY_GRANULARITY)); + /* Model Number */ + aml_append(pkg, aml_string("QBAT001")); /* Model Number */ + /* Serial Number */ + aml_append(pkg, aml_string("SN00000")); /* Serial Number */ + /* Battery Type */ + aml_append(pkg, aml_string("Virtual")); /* Battery Type */ + /* OEM Information */ + aml_append(pkg, aml_string("QEMU")); /* OEM Information */ + aml_append(method, aml_return(pkg)); + aml_append(dev, method); + + pkg =3D aml_package(4); + /* Battery State */ + aml_append(pkg, aml_int(0)); + /* Battery Present Rate */ + aml_append(pkg, aml_int(BATTERY_VAL_UNKNOWN)); + /* Battery Remaining Capacity */ + aml_append(pkg, aml_int(BATTERY_VAL_UNKNOWN)); + /* Battery Present Voltage */ + aml_append(pkg, aml_int(BATTERY_VAL_UNKNOWN)); + aml_append(dev, aml_name_decl("DBPR", pkg)); + + method =3D aml_method("_BST", 0, AML_NOTSERIALIZED); + aml_append(method, aml_store(aml_name("BSTA"), bat_state)); + aml_append(method, aml_store(aml_name("BRTE"), bat_rate)); + aml_append(method, aml_store(aml_name("BCRG"), bat_charge)); + aml_append(method, aml_store(bat_state, + aml_index(aml_name("DBPR"), aml_int(0= )))); + aml_append(method, aml_store(bat_rate, + aml_index(aml_name("DBPR"), aml_int(1= )))); + aml_append(method, aml_store(bat_charge, + aml_index(aml_name("DBPR"), aml_int(2= )))); + aml_append(method, aml_return(aml_name("DBPR"))); + aml_append(dev, method); + + aml_append(sb_scope, dev); + + /* Device Check */ + method =3D aml_method("\\_GPE._E07", 0, AML_NOTSERIALIZED); + aml_append(method, aml_notify(aml_name("\\_SB.BAT0"), aml_int(0x01= ))); + aml_append(dsdt, method); + + /* Status Change */ + method =3D aml_method("\\_GPE._E08", 0, AML_NOTSERIALIZED); + aml_append(method, aml_notify(aml_name("\\_SB.BAT0"), aml_int(0x80= ))); + aml_append(dsdt, method); + + /* Information Change */ + method =3D aml_method("\\_GPE._E09", 0, AML_NOTSERIALIZED); + aml_append(method, aml_notify(aml_name("\\_SB.BAT0"), aml_int(0x81= ))); + aml_append(dsdt, method); + } + aml_append(dsdt, sb_scope); =20 /* copy AML table into ACPI tables blob and patch header there */ diff --git a/include/hw/acpi/acpi_dev_interface.h b/include/hw/acpi/acpi_de= v_interface.h index 769ff55c7e..3d98d5636a 100644 --- a/include/hw/acpi/acpi_dev_interface.h +++ b/include/hw/acpi/acpi_dev_interface.h @@ -14,6 +14,7 @@ typedef enum { ACPI_NVDIMM_HOTPLUG_STATUS =3D 16, ACPI_VMGENID_CHANGE_STATUS =3D 32, ACPI_POWER_DOWN_STATUS =3D 64, + ACPI_BATTERY_CHANGE_STATUS =3D 128, } AcpiEventStatusBits; =20 #define TYPE_ACPI_DEVICE_IF "acpi-device-interface" diff --git a/include/hw/acpi/battery.h b/include/hw/acpi/battery.h new file mode 100644 index 0000000000..6224a97d9c --- /dev/null +++ b/include/hw/acpi/battery.h @@ -0,0 +1,43 @@ +/* + * QEMU emulated battery device. + * + * Copyright (c) 2019 Janus Technologies, Inc. (http://janustech.com) + * + * Authors: + * Leonid Bloch + * Marcel Apfelbaum + * Dmitry Fleytman + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory for details. + * + */ + +#ifndef HW_ACPI_BATTERY_H +#define HW_ACPI_BATTERY_H + +#define TYPE_BATTERY "battery" +#define BATTERY_IOPORT_PROP "ioport" +#define BATTERY_PATH_PROP "sysfs_path" +#define BATTERY_PROBE_STATE_INTERVAL "probe_interval" + +#define BATTERY_FULL_CAP 10000 /* mWh */ + +#define BATTERY_CAPACITY_OF_WARNING (BATTERY_FULL_CAP / 10) /* 10% */ +#define BATTERY_CAPACITY_OF_LOW (BATTERY_FULL_CAP / 25) /* 4% */ +#define BATTERY_CAPACITY_GRANULARITY (BATTERY_FULL_CAP / 100) /* 1% */ + +#define BATTERY_VAL_UNKNOWN 0xFFFFFFFF + +#define BATTERY_LEN 0x0C + +static inline uint16_t battery_port(void) +{ + Object *o =3D object_resolve_path_type("", TYPE_BATTERY, NULL); + if (!o) { + return 0; + } + return object_property_get_uint(o, BATTERY_IOPORT_PROP, NULL); +} + +#endif --=20 2.30.0 From nobody Tue Nov 18 14:09:00 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1611176425; cv=none; d=zohomail.com; s=zohoarc; b=LveElttEM68qvVfo5WqD2hNtVD7SMHdQ6Argp0klUoshVLgpxHdiA7vrx99MVbxmeCmCykJKsmDbDmKp/j9cRDy4jLFFO2OPn9eQlK2mKNZrzfpHundFBRsYr9xrIq/OzbG4tnZkagNa6O+duWQhcDDhsqzyD9DTRBAj5jKai7o= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1611176425; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=n+tVslsM8xLBzMjIoyI6BiAP2/abaKFwOyL/G4B8rLM=; b=RUAGVugpkJFmPny6Mum6h9ud66IS2U8s3SnYxV7lE7FVR74WGYSy7As7nK7Od3J7tVeQ1Rw1KYvANCLTFoAVKhn07MVidDrK50t1IFxSAAbz0xiKFdiFhf7MQQ6Fc/MNLeuZ3iNM+kabsB+t+CaayJEWZKVqSfZmSNqoTTIiIU8= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 161117642592060.67540734691943; Wed, 20 Jan 2021 13:00:25 -0800 (PST) Received: from localhost ([::1]:44916 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1l2KaW-000194-Ss for importer@patchew.org; Wed, 20 Jan 2021 16:00:24 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:35732) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1l2KVe-0005L2-Qt for qemu-devel@nongnu.org; Wed, 20 Jan 2021 15:55:22 -0500 Received: from mail-wm1-x32c.google.com ([2a00:1450:4864:20::32c]:51423) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1l2KVb-0006LM-Uh for qemu-devel@nongnu.org; Wed, 20 Jan 2021 15:55:22 -0500 Received: by mail-wm1-x32c.google.com with SMTP id m2so3961718wmm.1 for ; Wed, 20 Jan 2021 12:55:19 -0800 (PST) Received: from lb-xps.. ([176.230.215.91]) by smtp.gmail.com with ESMTPSA id k10sm5614823wrq.38.2021.01.20.12.55.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 20 Jan 2021 12:55:17 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=n+tVslsM8xLBzMjIoyI6BiAP2/abaKFwOyL/G4B8rLM=; b=PMAvcDtOUXM9GYNIerxTkYhgLHjQN6veWvSf1/EOjDDj9rvwn3AfOs7CmT+1z88+SV 8dhMee3Om3OxIN2QVNzGIZ/UjbMSG2XVLHY/bVkhB+oFQoHK9vz/KYpgbUbshLN/VMBq 4CfQ4Lm5I12KLwUpcJ5UqwsWoMDtAhb57GknlwJiagH567OPLtT9kQKqi7PCS5YaYBET /RixTNOJIlBCwN65xg31PJk8ncXj2cbVyBOwA5Tfxcw4NisqyXzcO2Zx+GyC8dfkToze DzvBVVtEN+fxF8aB8a7VZci8gOKvfO7kTQM3A2fZ9kcrm8H0C6oqgG3fmdicaSwAJId7 BI8A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=n+tVslsM8xLBzMjIoyI6BiAP2/abaKFwOyL/G4B8rLM=; b=avBLu23k4znR1WMZa705Su+vosRp+X9LoP6NI/kGwH4B2bsOoliOi+rY813hor2J+/ a8rHAqOPoNA3dPnJKl4ZS1B0RbVIZANwvlFsUQPo3Gm45YdLSlk5kElR1bi7EdfI8zoi ntBCrDJojP32+CfMg9wIHWRLVWw1WVADODVIUdtrJs+jOKSNzWlAOpvMs8StyqVNwgB/ c78jwjqx80t45KeekoGYjKsPM3FJpxkAHyEajHuUdf+iQwWfnUsRk6M9XwL18paa7/yz iPzjaWcthT29n5K+kjp8NXAoeGOSOCMv5KndNL2Kzx7igX8bBhv0TxOlk3cAmhqlvDMn dcYA== X-Gm-Message-State: AOAM530y8dtIx/Ry8lRAZYRctRBb4ZJ6P2sIAXjZ/mxerl9TM+dj9dPn Qxle2+wdkE1gzkia7iAt+kE= X-Google-Smtp-Source: ABdhPJyL/tnSPozD1BtYlv+4yMNWB9H+e5XYfxl3NBz77etZ9SHE0VPEnJK7tHi2HdqRc3aXRNeUFw== X-Received: by 2002:a1c:18d:: with SMTP id 135mr4775510wmb.33.1611176117882; Wed, 20 Jan 2021 12:55:17 -0800 (PST) From: Leonid Bloch To: "Michael S . Tsirkin" , Igor Mammedov , Paolo Bonzini , Richard Henderson , Eduardo Habkost , Marcel Apfelbaum Subject: [PATCH 3/4] hw/acpi: Introduce the QEMU AC adapter Date: Wed, 20 Jan 2021 22:55:00 +0200 Message-Id: <20210120205501.33918-4-lb.workbox@gmail.com> X-Mailer: git-send-email 2.30.0 In-Reply-To: <20210120205501.33918-1-lb.workbox@gmail.com> References: <20210120205501.33918-1-lb.workbox@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2a00:1450:4864:20::32c; envelope-from=lb.workbox@gmail.com; helo=mail-wm1-x32c.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Leonid Bloch , qemu-devel@nongnu.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: pass (identity @gmail.com) Content-Type: text/plain; charset="utf-8" The AC adapter device communicates the host's AC adapter state to the guest. The probing of the host's AC adapter state occurs on guest ACPI requests, as well as on timed intervals. If a change of the host's AC adapter state is detected on one of the timed probes (connected/disconnecte= d) an ACPI notification is sent to the guest, so it will be able to update its AC adapter status accordingly. The time interval between the periodic probes is 2 seconds by default. A property 'probe_interval' allows to modify this value. The value should be provided in milliseconds. A zero value disables the periodic probes, and makes the AC adapter state updates occur on guest requests only. The host's AC adapter information is taken from the sysfs AC adapter data, located in: /sys/class/power_supply/[device of type "Mains"] 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, a 'sysfs_path' property allows to override the default one. Signed-off-by: Leonid Bloch --- MAINTAINERS | 5 + docs/specs/acad.txt | 24 ++ hw/acpi/Kconfig | 4 + hw/acpi/acad.c | 318 +++++++++++++++++++++++++++ hw/acpi/meson.build | 1 + hw/acpi/trace-events | 5 + hw/i386/Kconfig | 1 + hw/i386/acpi-build.c | 52 +++++ include/hw/acpi/acad.h | 37 ++++ include/hw/acpi/acpi_dev_interface.h | 1 + 10 files changed, 448 insertions(+) create mode 100644 docs/specs/acad.txt create mode 100644 hw/acpi/acad.c create mode 100644 include/hw/acpi/acad.h diff --git a/MAINTAINERS b/MAINTAINERS index 33eea28c22..1a3fc1dd56 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2146,6 +2146,11 @@ M: Leonid Bloch S: Maintained F: hw/acpi/battery.* =20 +AC Adapter +M: Leonid Bloch +S: Maintained +F: hw/acpi/acad.* + Subsystems ---------- Audio diff --git a/docs/specs/acad.txt b/docs/specs/acad.txt new file mode 100644 index 0000000000..0d563a7b50 --- /dev/null +++ b/docs/specs/acad.txt @@ -0,0 +1,24 @@ +AC ADAPTER DEVICE +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +The AC adapter device communicates the host's AC adapter state to the +guest. The probing of the host's AC adapter state occurs on guest ACPI +requests, as well as on timed intervals. If a change of the host's AC +adapter state is detected on one of the timed probes (connected/disconnect= ed) +an ACPI notification is sent to the guest, so it will be able to +update its AC adapter status accordingly. + +The time interval between the periodic probes is 2 s by default. +A property 'probe_interval' allows to modify this value. The value +should be provided in milliseconds. A zero value disables the periodic +probes, and makes the AC adapter state updates occur on guest requests +only. + +The host's AC adapter information is taken from the sysfs AC adapter +data, located in: + +/sys/class/power_supply/[device of type "Mains"] + +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, a 'sysfs_path' +property allows to override the default one. diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig index 6b4c41037a..d35331fbf7 100644 --- a/hw/acpi/Kconfig +++ b/hw/acpi/Kconfig @@ -45,4 +45,8 @@ config BATTERY bool depends on ACPI =20 +config AC_ADAPTER + bool + depends on ACPI + config ACPI_HW_REDUCED diff --git a/hw/acpi/acad.c b/hw/acpi/acad.c new file mode 100644 index 0000000000..279452e95f --- /dev/null +++ b/hw/acpi/acad.c @@ -0,0 +1,318 @@ +/* + * QEMU emulated AC adapter device. + * + * Copyright (c) 2019 Janus Technologies, Inc. (http://janustech.com) + * + * Authors: + * Leonid Bloch + * Marcel Apfelbaum + * Dmitry Fleytman + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory for details. + * + */ + +#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/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 =3D 0, + AC_ADAPTER_ONLINE =3D 1, +}; + +typedef struct ACADState { + ISADevice dev; + MemoryRegion io; + uint16_t ioport; + uint8_t state; + + QEMUTimer *probe_state_timer; + uint64_t probe_state_interval; + + char *acad_path; +} ACADState; + +static const char *online_file =3D "online"; +static const char *type_file =3D "type"; + +static inline bool acad_file_accessible(char *path, const char *file) +{ + char full_path[PATH_MAX]; + int path_len; + + path_len =3D snprintf(full_path, PATH_MAX, "%s/%s", path, file); + if (path_len < 0 || path_len >=3D PATH_MAX) { + return false; + } + + if (access(full_path, R_OK) =3D=3D 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 =3D snprintf(file_path, PATH_MAX, "%s/%s", s->acad_path, + online_file); + if (path_len < 0 || path_len >=3D PATH_MAX) { + warn_report("Could not read the AC adapter state."); + return; + } + + ff =3D fopen(file_path, "r"); + if (ff =3D=3D 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 =3D val; + break; + default: + warn_report("AC adapter state undetermined."); + } + } + fclose(ff); +} + +static void acad_get_dynamic_status(ACADState *s) +{ + acad_get_state(s); + + trace_acad_get_dynamic_status(s->state); +} + +static void acad_probe_state(void *opaque) +{ + ACADState *s =3D opaque; + + uint8_t state_before =3D s->state; + + acad_get_dynamic_status(s); + + if (state_before !=3D s->state) { + Object *obj =3D 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->probe_state_interval > 0) { + s->probe_state_timer =3D timer_new_ms(QEMU_CLOCK_VIRTUAL, + acad_probe_state, s); + timer_mod(s->probe_state_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTU= AL) + + 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 =3D snprintf(type_path, PATH_MAX, "%s/%s", path, type_file); + if (path_len < 0 || path_len >=3D PATH_MAX) { + return false; + } + + ff =3D fopen(type_path, "r"); + if (ff =3D=3D NULL) { + return false; + } + + if (fgets(val, MAX_ALLOWED_TYPE_LENGTH, ff) =3D=3D NULL) { + fclose(ff); + return false; + } else { + val[strcspn(val, "\n")] =3D 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 =3D 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 =3D opendir(SYSFS_PATH); + if (dir =3D=3D NULL) { + return false; + } + + ent =3D readdir(dir); + while (ent !=3D NULL) { + if (ent->d_name[0] !=3D '.') { + path_len =3D snprintf(bp, PATH_MAX, "%s/%s", SYSFS_PATH, + ent->d_name); + if (path_len < 0 || path_len >=3D 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 =3D readdir(dir); + } + closedir(dir); + + return false; +} + +static void acad_realize(DeviceState *dev, Error **errp) +{ + ISADevice *d =3D ISA_DEVICE(dev); + ACADState *s =3D AC_ADAPTER_DEVICE(dev); + FWCfgState *fw_cfg =3D fw_cfg_find(); + uint16_t *acad_port; + char err_details[32] =3D {}; + + trace_acad_realize(); + + if (!s->acad_path) { + strcpy(err_details, " Try using 'sysfs_path=3D'"); + } + + 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 =3D g_malloc(sizeof(*acad_port)); + *acad_port =3D cpu_to_le16(s->ioport); + fw_cfg_add_file(fw_cfg, "etc/acad-port", acad_port, + sizeof(*acad_port)); +} + +static Property acad_device_properties[] =3D { + DEFINE_PROP_UINT16(AC_ADAPTER_IOPORT_PROP, ACADState, ioport, 0x53c), + DEFINE_PROP_UINT64(AC_ADAPTER_PROBE_STATE_INTERVAL, ACADState, + probe_state_interval, 2000), + DEFINE_PROP_STRING(AC_ADAPTER_PATH_PROP, ACADState, acad_path), + DEFINE_PROP_END_OF_LIST(), +}; + +static const VMStateDescription acad_vmstate =3D { + .name =3D "acad", + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (VMStateField[]) { + VMSTATE_UINT16(ioport, ACADState), + VMSTATE_UINT64(probe_state_interval, ACADState), + VMSTATE_END_OF_LIST() + } +}; + +static void acad_class_init(ObjectClass *class, void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(class); + + dc->realize =3D acad_realize; + device_class_set_props(dc, acad_device_properties); + dc->vmsd =3D &acad_vmstate; +} + +static uint64_t acad_ioport_read(void *opaque, hwaddr addr, unsigned size) +{ + ACADState *s =3D 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 =3D { + .read =3D acad_ioport_read, + .impl =3D { + .min_access_size =3D 1, + .max_access_size =3D 1, + }, +}; + +static void acad_instance_init(Object *obj) +{ + ACADState *s =3D AC_ADAPTER_DEVICE(obj); + + memory_region_init_io(&s->io, obj, &acad_ops, s, "acad", + AC_ADAPTER_LEN); +} + +static const TypeInfo acad_info =3D { + .name =3D TYPE_AC_ADAPTER, + .parent =3D TYPE_ISA_DEVICE, + .instance_size =3D sizeof(ACADState), + .class_init =3D acad_class_init, + .instance_init =3D acad_instance_init, +}; + +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 485eab33b6..2ae756c6ef 100644 --- a/hw/acpi/meson.build +++ b/hw/acpi/meson.build @@ -20,6 +20,7 @@ acpi_ss.add(when: 'CONFIG_IPMI', if_true: files('ipmi.c')= , if_false: files('ipmi acpi_ss.add(when: 'CONFIG_PC', if_false: files('acpi-x86-stub.c')) acpi_ss.add(when: 'CONFIG_TPM', if_true: files('tpm.c')) acpi_ss.add(when: 'CONFIG_BATTERY', if_true: files('battery.c')) +acpi_ss.add(when: 'CONFIG_AC_ADAPTER', if_true: files('acad.c')) softmmu_ss.add(when: 'CONFIG_ACPI', if_false: files('acpi-stub.c', 'aml-bu= ild-stub.c')) softmmu_ss.add_all(when: 'CONFIG_ACPI', if_true: acpi_ss) softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('acpi-stub.c', 'aml-buil= d-stub.c', diff --git a/hw/acpi/trace-events b/hw/acpi/trace-events index 5d2c606496..496a282cdc 100644 --- a/hw/acpi/trace-events +++ b/hw/acpi/trace-events @@ -58,3 +58,8 @@ tco_timer_expired(int timeouts_no, bool strap, bool no_re= boot) "timeouts_no=3D%d n 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 b081be7a0f..483b3ccec8 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -24,6 +24,7 @@ config PC imply VGA_PCI imply VIRTIO_VGA imply BATTERY + imply AC_ADAPTER select FDC select I8259 select I8254 diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index ecd5380f82..96dbeed22f 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -36,6 +36,7 @@ #include "hw/acpi/acpi.h" #include "hw/acpi/cpu.h" #include "hw/acpi/battery.h" +#include "hw/acpi/acad.h" #include "hw/nvram/fw_cfg.h" #include "hw/acpi/bios-linker-loader.h" #include "hw/isa/isa.h" @@ -114,6 +115,7 @@ typedef struct AcpiMiscInfo { unsigned dsdt_size; uint16_t pvpanic_port; uint16_t battery_port; + uint16_t acad_port; uint16_t applesmc_io_base; } AcpiMiscInfo; =20 @@ -280,6 +282,7 @@ static void acpi_get_misc_info(AcpiMiscInfo *info) info->tpm_version =3D tpm_get_version(tpm_find()); info->pvpanic_port =3D pvpanic_port(); info->battery_port =3D battery_port(); + info->acad_port =3D acad_port(); info->applesmc_io_base =3D applesmc_port(); } =20 @@ -1728,6 +1731,55 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, aml_append(dsdt, method); } =20 + if (misc->acad_port) { + Aml *acad_state =3D aml_local(0); + + dev =3D 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(misc->acad_port), + AC_ADAPTER_LEN)); + field =3D aml_field("ACST", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE= ); + aml_append(field, aml_named_field("PWRS", 8)); + aml_append(dev, field); + + method =3D 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 =3D aml_method("_PCL", 0, AML_NOTSERIALIZED); + pkg =3D aml_package(1); + aml_append(pkg, aml_name("_SB")); + aml_append(method, aml_return(pkg)); + aml_append(dev, method); + + method =3D aml_method("_PIF", 0, AML_NOTSERIALIZED); + pkg =3D 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); + + /* Status Change */ + method =3D aml_method("\\_GPE._E0A", 0, AML_NOTSERIALIZED); + aml_append(method, aml_notify(aml_name("\\_SB.ADP0"), aml_int(0x80= ))); + aml_append(dsdt, method); + } + aml_append(dsdt, sb_scope); =20 /* copy AML table into ACPI tables blob and patch header there */ diff --git a/include/hw/acpi/acad.h b/include/hw/acpi/acad.h new file mode 100644 index 0000000000..be61c57064 --- /dev/null +++ b/include/hw/acpi/acad.h @@ -0,0 +1,37 @@ +/* + * QEMU emulated AC adapter device. + * + * Copyright (c) 2019 Janus Technologies, Inc. (http://janustech.com) + * + * Authors: + * Leonid Bloch + * Marcel Apfelbaum + * Dmitry Fleytman + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory for details. + * + */ + +#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 + +static inline uint16_t acad_port(void) +{ + Object *o =3D object_resolve_path_type("", TYPE_AC_ADAPTER, NULL); + if (!o) { + return 0; + } + return object_property_get_uint(o, AC_ADAPTER_IOPORT_PROP, NULL); +} + +#endif diff --git a/include/hw/acpi/acpi_dev_interface.h b/include/hw/acpi/acpi_de= v_interface.h index 3d98d5636a..b577a4db07 100644 --- a/include/hw/acpi/acpi_dev_interface.h +++ b/include/hw/acpi/acpi_dev_interface.h @@ -15,6 +15,7 @@ typedef enum { ACPI_VMGENID_CHANGE_STATUS =3D 32, ACPI_POWER_DOWN_STATUS =3D 64, ACPI_BATTERY_CHANGE_STATUS =3D 128, + ACPI_AC_ADAPTER_CHANGE_STATUS =3D 1024, } AcpiEventStatusBits; =20 #define TYPE_ACPI_DEVICE_IF "acpi-device-interface" --=20 2.30.0 From nobody Tue Nov 18 14:09:00 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1611176273; cv=none; d=zohomail.com; s=zohoarc; b=efn2PNOl1HuyGnC2NZoqQoIyRvVvYoAAsKtKUH/p7j7ROTUIxrWIwkGP9sQBqVauUaTeLvJFGrCnMvOs4QRrxdcXHJirJPcYw9tRbNXblhGuWaAOQr/n0IBUDxB1xrXCc+TtweqIR+YM6BNdP9Ll9q+2/8/0dVGUwn47w3K/9iQ= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1611176273; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=+50sXcYbgpoJeb4PTQoSEae1wYSOROdopOFpjZG7mVc=; b=Nf4Q8S8KcBDYxU28SIt02/4xjcAG0S6p+pGvyjQ8TiPvxnp9Pfp4vFn9+etc09Mv+gOkKKaOIlo7KK/aLPRTFpUeWhOvEKpgfe0+JMSRsrtjERItqWpZt4LuSZr3C6n+eUHcjN0KnEU3oZal9uuAJfdskWqcBo8hAC6Wce/1AvY= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1611176273769989.0308478394777; Wed, 20 Jan 2021 12:57:53 -0800 (PST) Received: from localhost ([::1]:39888 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1l2KY4-0007SV-LR for importer@patchew.org; Wed, 20 Jan 2021 15:57:52 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:35734) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1l2KVf-0005N7-LJ for qemu-devel@nongnu.org; Wed, 20 Jan 2021 15:55:23 -0500 Received: from mail-wr1-x433.google.com ([2a00:1450:4864:20::433]:37966) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1l2KVc-0006Lm-T2 for qemu-devel@nongnu.org; Wed, 20 Jan 2021 15:55:23 -0500 Received: by mail-wr1-x433.google.com with SMTP id a9so20940625wrt.5 for ; Wed, 20 Jan 2021 12:55:20 -0800 (PST) Received: from lb-xps.. ([176.230.215.91]) by smtp.gmail.com with ESMTPSA id k10sm5614823wrq.38.2021.01.20.12.55.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 20 Jan 2021 12:55:19 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=+50sXcYbgpoJeb4PTQoSEae1wYSOROdopOFpjZG7mVc=; b=MDAmJi4n4wSyDEXYkwOJmZ5vojj6XTGLiWXIA0l5JIzl1ZxNE6Kv1ZMZeuxqEXBn6Z hC/2a83X2jCPoWSh2rE3u7EWw3sgIY0pnTiCJ32uxPtu/jFjU/1U3LRPhy/sJChngTQc Rguvgs0BjWLmQXScGUHd7ZyK0VA0ugKadyZXjxD/MDgU6hJMck45zxR9YB1fJy5+V44g bg+9Ac4S6UpvJWmNaQxPGvuEmR+XTC7nd9U/4+zvnt5VhNYaV3fSXKqzgyYen4pSwe9C Za+HzS9pape8KsXQ6W72MWb7yF17Pa3q0kaKG18QeID+MtUgZsDNNTgE+5lP7vj55J/J jNtA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=+50sXcYbgpoJeb4PTQoSEae1wYSOROdopOFpjZG7mVc=; b=NF9s1wakL8AL5K1GX0kp2md4QcnXjIv/OlPcPa9P66xrPWKjjiz7WogX3OFxZPOYey xdRC+GH21AkRYySvzUBXsAPGd+pCf+0ehuhWgCsAHrGdk+8xp/3DjSUWF1D5zFpstgcN bk4LMwdKLJKBe8STHx6ccn52b13eYCiPF8Qh9W6AnGYKIYBMrybIHGHgn7Jgf+7qo4EP GqK2g5rChP1p8xxXVrtiL1YIpWNKYbMJOchmRIQxPtoE64cKqBldPR1KNOcLzCltRMWL mPNf9WvBvExjeBcT2y/Vqy5xnwCcG414UjcuRB1bRshDlyksD6bUMkPgPUG6KYSM5yVN tpIA== X-Gm-Message-State: AOAM5320uaz+5N1Oj627vD3okMNNvdlMxENWX/Nl/tThgzBoo8KCHbLW s0TjgPB4/2JcNG6X4AQdOEy8KtOdhvyetAQy/Ao= X-Google-Smtp-Source: ABdhPJym5Sbuy429QB/QPJZZ0J3aJyQ7S3WYWYNZHhh/0KAUpaOG5f+M1e758jjzNLrnJG0xzqLRAA== X-Received: by 2002:adf:c106:: with SMTP id r6mr10723756wre.175.1611176119652; Wed, 20 Jan 2021 12:55:19 -0800 (PST) From: Leonid Bloch To: "Michael S . Tsirkin" , Igor Mammedov , Paolo Bonzini , Richard Henderson , Eduardo Habkost , Marcel Apfelbaum Subject: [PATCH 4/4] hw/acpi: Introduce the QEMU lid button Date: Wed, 20 Jan 2021 22:55:01 +0200 Message-Id: <20210120205501.33918-5-lb.workbox@gmail.com> X-Mailer: git-send-email 2.30.0 In-Reply-To: <20210120205501.33918-1-lb.workbox@gmail.com> References: <20210120205501.33918-1-lb.workbox@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2a00:1450:4864:20::433; envelope-from=lb.workbox@gmail.com; helo=mail-wr1-x433.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Leonid Bloch , qemu-devel@nongnu.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: pass (identity @gmail.com) Content-Type: text/plain; charset="utf-8" The button device communicates the host's "lid button" state to the guest. The probing of the host's lid button state occurs on timed intervals. If a change of the host's lid button state is detected (open/closed) an ACPI notification is sent to the guest, so it will be able to act accordingly. The time interval between the periodic probes is 2 s by default. A property 'probe_interval' allows to modify this value. The value is provided in milliseconds. The host's lid button information is taken from: /proc/acpi/button/lid/*/state And this file is expected to be formatted as: 'state: open' (if the lid is open) or: 'state: closed' (if the lid is closed) These are based on the Linux 'button' driver. If the above procfs path differs, or even if a "fake" host lid button is to be provided, a 'procfs_path' property allows to override the default path. Usage example (default values): '-device button' Is the same as: '-device button,procfs_path=3D/proc/acpi/button,probe_interval=3D2000' Signed-off-by: Leonid Bloch --- MAINTAINERS | 5 + docs/specs/button.txt | 35 +++ hw/acpi/Kconfig | 4 + hw/acpi/button.c | 327 +++++++++++++++++++++++++++ hw/acpi/meson.build | 1 + hw/acpi/trace-events | 5 + hw/i386/Kconfig | 1 + hw/i386/acpi-build.c | 29 +++ include/hw/acpi/acpi_dev_interface.h | 1 + include/hw/acpi/button.h | 35 +++ 10 files changed, 443 insertions(+) create mode 100644 docs/specs/button.txt create mode 100644 hw/acpi/button.c create mode 100644 include/hw/acpi/button.h diff --git a/MAINTAINERS b/MAINTAINERS index 1a3fc1dd56..72bead7023 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2151,6 +2151,11 @@ M: Leonid Bloch S: Maintained F: hw/acpi/acad.* =20 +Button +M: Leonid Bloch +S: Maintained +F: hw/acpi/button.* + Subsystems ---------- Audio diff --git a/docs/specs/button.txt b/docs/specs/button.txt new file mode 100644 index 0000000000..48e9f3388d --- /dev/null +++ b/docs/specs/button.txt @@ -0,0 +1,35 @@ +BUTTON DEVICE +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +The button device communicates the host's "lid button" state to the guest. +The probing of the host's lid button state occurs on timed intervals. +If a change of the host's lid button state is detected (open/closed) +an ACPI notification is sent to the guest, so it will be able to act +accordingly. + +The time interval between the periodic probes is 2 s by default. +A property 'probe_interval' allows to modify this value. The value +is provided in milliseconds. + +The host's lid button information is taken from: + +/proc/acpi/button/lid/*/state + +And this file is expected to be formatted as: +'state: open' (if the lid is open) +or: +'state: closed' (if the lid is closed) + +These are based on the Linux 'button' driver. + +If the above procfs path differs, or even if a "fake" host lid button +is to be provided, a 'procfs_path' property allows to override the +default path. + +Usage example (default values): + +'-device button' + +Is the same as: + +'-device button,procfs_path=3D/proc/acpi/button,probe_interval=3D2000' diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig index d35331fbf7..0e78560f0f 100644 --- a/hw/acpi/Kconfig +++ b/hw/acpi/Kconfig @@ -49,4 +49,8 @@ config AC_ADAPTER bool depends on ACPI =20 +config BUTTON + bool + depends on ACPI + config ACPI_HW_REDUCED diff --git a/hw/acpi/button.c b/hw/acpi/button.c new file mode 100644 index 0000000000..edca46ce6d --- /dev/null +++ b/hw/acpi/button.c @@ -0,0 +1,327 @@ +/* + * QEMU emulated lid button device + * + * Copyright (c) 2019 Janus Technologies, Inc. (http://janustech.com) + * + * Authors: + * Leonid Bloch + * Marcel Apfelbaum + * Dmitry Fleytman + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory for details. + * + */ + +#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/button.h" + +#define BUTTON_DEVICE(obj) OBJECT_CHECK(BUTTONState, (obj), \ + TYPE_BUTTON) + +#define BUTTON_STA_ADDR 0 + +#define PROCFS_PATH "/proc/acpi/button" +#define LID_DIR "lid" +#define LID_STATE_FILE "state" +#define MIN_BUTTON_PROBE_INTERVAL 10 /* ms */ +#define MAX_ALLOWED_LINE_LENGTH 32 /* For convenience when comparing */ + +enum { + LID_CLOSED =3D 0, + LID_OPEN =3D 1, +}; + +static const char *lid_state[] =3D { "closed", "open" }; + +typedef struct BUTTONState { + ISADevice dev; + MemoryRegion io; + uint16_t ioport; + uint8_t lid_state; + + QEMUTimer *probe_state_timer; + uint64_t probe_state_interval; + + char *button_path; + char lid_dir[MAX_ALLOWED_LINE_LENGTH]; +} BUTTONState; + +static inline bool button_file_accessible(char *path, const char *dir, + char *subdir, const char *file) +{ + char full_path[PATH_MAX]; + int path_len; + + path_len =3D snprintf(full_path, PATH_MAX, "%s/%s/%s/%s", path, dir, s= ubdir, + file); + if (path_len < 0 || path_len >=3D PATH_MAX) { + return false; + } + + if (access(full_path, R_OK) =3D=3D 0) { + return true; + } + return false; +} + +static void button_get_lid_state(BUTTONState *s) +{ + char file_path[PATH_MAX]; + int path_len; + char line[MAX_ALLOWED_LINE_LENGTH]; + FILE *ff; + + path_len =3D snprintf(file_path, PATH_MAX, "%s/%s/%s/%s", s->button_pa= th, + LID_DIR, s->lid_dir, LID_STATE_FILE); + if (path_len < 0 || path_len >=3D PATH_MAX) { + warn_report("Could not read the lid state."); + return; + } + + ff =3D fopen(file_path, "r"); + if (ff =3D=3D NULL) { + warn_report("Could not read the lid state."); + return; + } + + if (fgets(line, MAX_ALLOWED_LINE_LENGTH, ff) =3D=3D NULL) { + warn_report("Lid state unreadable."); + } else { + if (strstr(line, lid_state[LID_OPEN]) !=3D NULL) { + s->lid_state =3D LID_OPEN; + } else if (strstr(line, lid_state[LID_CLOSED]) !=3D NULL) { + s->lid_state =3D LID_CLOSED; + } else { + warn_report("Lid state undetermined."); + } + } + + fclose(ff); +} + +static void button_get_dynamic_status(BUTTONState *s) +{ + trace_button_get_dynamic_status(); + + button_get_lid_state(s); +} + +static void button_probe_state(void *opaque) +{ + BUTTONState *s =3D opaque; + + uint8_t lid_state_before =3D s->lid_state; + + button_get_dynamic_status(s); + + if (lid_state_before !=3D s->lid_state) { + Object *obj =3D object_resolve_path_type("", TYPE_ACPI_DEVICE_IF, = NULL); + acpi_send_event(DEVICE(obj), ACPI_BUTTON_CHANGE_STATUS); + } + timer_mod(s->probe_state_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + + s->probe_state_interval); +} + +static void button_probe_state_timer_init(BUTTONState *s) +{ + if (s->probe_state_interval > 0) { + s->probe_state_timer =3D timer_new_ms(QEMU_CLOCK_VIRTUAL, + button_probe_state, s); + timer_mod(s->probe_state_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTU= AL) + + s->probe_state_interval); + } +} + +static inline bool button_verify_lid_procfs(char *path, char *lid_subdir) +{ + return button_file_accessible(path, LID_DIR, lid_subdir, LID_STATE_FIL= E); +} + +static bool button_get_lid_dir(BUTTONState *s, char *path) +{ + DIR *dir; + char lid_path[PATH_MAX]; + int path_len; + struct dirent *ent; + + path_len =3D snprintf(lid_path, PATH_MAX, "%s/%s", path, LID_DIR); + if (path_len < 0 || path_len >=3D PATH_MAX) { + return false; + } + + dir =3D opendir(lid_path); + if (dir =3D=3D NULL) { + return false; + } + + ent =3D readdir(dir); + while (ent !=3D NULL) { + if (ent->d_name[0] !=3D '.') { + if (button_verify_lid_procfs(path, ent->d_name)) { + path_len =3D snprintf(s->lid_dir, strlen(ent->d_name) + 1,= "%s", + ent->d_name); + if (path_len < 0 || path_len > strlen(ent->d_name)) { + return false; + } + closedir(dir); + return true; + } + } + ent =3D readdir(dir); + } + closedir(dir); + return false; +} + +static bool get_button_path(DeviceState *dev) +{ + BUTTONState *s =3D BUTTON_DEVICE(dev); + char procfs_path[PATH_MAX]; + int path_len; + + if (s->button_path) { + path_len =3D snprintf(procfs_path, strlen(s->button_path) + 1, "%s= ", + s->button_path); + if (path_len < 0 || path_len > strlen(s->button_path)) { + return false; + } + } else { + path_len =3D snprintf(procfs_path, sizeof(PROCFS_PATH), "%s", + PROCFS_PATH); + if (path_len < 0 || path_len >=3D sizeof(PROCFS_PATH)) { + return false; + } + } + + if (button_get_lid_dir(s, procfs_path)) { + qdev_prop_set_string(dev, BUTTON_PATH_PROP, procfs_path); + return true; + } + + return false; +} + +static void button_realize(DeviceState *dev, Error **errp) +{ + ISADevice *d =3D ISA_DEVICE(dev); + BUTTONState *s =3D BUTTON_DEVICE(dev); + FWCfgState *fw_cfg =3D fw_cfg_find(); + uint16_t *button_port; + char err_details[32] =3D {}; + + trace_button_realize(); + + if (s->probe_state_interval < MIN_BUTTON_PROBE_INTERVAL) { + error_setg(errp, "'probe_state_interval' must be greater than %d m= s", + MIN_BUTTON_PROBE_INTERVAL); + return; + } + + if (!s->button_path) { + strcpy(err_details, " Try using 'procfs_path=3D'"); + } + + if (!get_button_path(dev)) { + error_setg(errp, "Button procfs path not found or unreadable.%s", + err_details); + return; + } + + isa_register_ioport(d, &s->io, s->ioport); + + button_probe_state_timer_init(s); + + if (!fw_cfg) { + return; + } + + button_port =3D g_malloc(sizeof(*button_port)); + *button_port =3D cpu_to_le16(s->ioport); + fw_cfg_add_file(fw_cfg, "etc/button-port", button_port, + sizeof(*button_port)); +} + +static Property button_device_properties[] =3D { + DEFINE_PROP_UINT16(BUTTON_IOPORT_PROP, BUTTONState, ioport, 0x53d), + DEFINE_PROP_UINT64(BUTTON_PROBE_STATE_INTERVAL, BUTTONState, + probe_state_interval, 2000), + DEFINE_PROP_STRING(BUTTON_PATH_PROP, BUTTONState, button_path), + DEFINE_PROP_END_OF_LIST(), +}; + +static const VMStateDescription button_vmstate =3D { + .name =3D "button", + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (VMStateField[]) { + VMSTATE_UINT16(ioport, BUTTONState), + VMSTATE_UINT64(probe_state_interval, BUTTONState), + VMSTATE_END_OF_LIST() + } +}; + +static void button_class_init(ObjectClass *class, void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(class); + + dc->realize =3D button_realize; + device_class_set_props(dc, button_device_properties); + dc->vmsd =3D &button_vmstate; +} + +static uint64_t button_ioport_read(void *opaque, hwaddr addr, unsigned siz= e) +{ + BUTTONState *s =3D opaque; + + button_get_dynamic_status(s); + + switch (addr) { + case BUTTON_STA_ADDR: + return s->lid_state; + default: + warn_report("Button: guest read unknown value."); + trace_button_ioport_read_unknown(); + return 0; + } +} + +static const MemoryRegionOps button_ops =3D { + .read =3D button_ioport_read, + .impl =3D { + .min_access_size =3D 1, + .max_access_size =3D 1, + }, +}; + +static void button_instance_init(Object *obj) +{ + BUTTONState *s =3D BUTTON_DEVICE(obj); + + memory_region_init_io(&s->io, obj, &button_ops, s, "button", + BUTTON_LEN); +} + +static const TypeInfo button_info =3D { + .name =3D TYPE_BUTTON, + .parent =3D TYPE_ISA_DEVICE, + .instance_size =3D sizeof(BUTTONState), + .class_init =3D button_class_init, + .instance_init =3D button_instance_init, +}; + +static void button_register_types(void) +{ + type_register_static(&button_info); +} + +type_init(button_register_types) diff --git a/hw/acpi/meson.build b/hw/acpi/meson.build index 2ae756c6ef..0f66766dd3 100644 --- a/hw/acpi/meson.build +++ b/hw/acpi/meson.build @@ -21,6 +21,7 @@ acpi_ss.add(when: 'CONFIG_PC', if_false: files('acpi-x86-= stub.c')) acpi_ss.add(when: 'CONFIG_TPM', if_true: files('tpm.c')) acpi_ss.add(when: 'CONFIG_BATTERY', if_true: files('battery.c')) acpi_ss.add(when: 'CONFIG_AC_ADAPTER', if_true: files('acad.c')) +acpi_ss.add(when: 'CONFIG_BUTTON', if_true: files('button.c')) softmmu_ss.add(when: 'CONFIG_ACPI', if_false: files('acpi-stub.c', 'aml-bu= ild-stub.c')) softmmu_ss.add_all(when: 'CONFIG_ACPI', if_true: acpi_ss) softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('acpi-stub.c', 'aml-buil= d-stub.c', diff --git a/hw/acpi/trace-events b/hw/acpi/trace-events index 496a282cdc..7554f0c8b6 100644 --- a/hw/acpi/trace-events +++ b/hw/acpi/trace-events @@ -63,3 +63,8 @@ battery_ioport_read_unknown(void) "Battery read unknown" 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" + +# button.c +button_realize(void) "Button device realize entry" +button_get_dynamic_status(void) "Button read dynamic status entry" +button_ioport_read_unknown(void) "Button read unknown" diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index 483b3ccec8..2a09e6f742 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -25,6 +25,7 @@ config PC imply VIRTIO_VGA imply BATTERY imply AC_ADAPTER + imply BUTTON select FDC select I8259 select I8254 diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 96dbeed22f..ee5537a1b4 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -37,6 +37,7 @@ #include "hw/acpi/cpu.h" #include "hw/acpi/battery.h" #include "hw/acpi/acad.h" +#include "hw/acpi/button.h" #include "hw/nvram/fw_cfg.h" #include "hw/acpi/bios-linker-loader.h" #include "hw/isa/isa.h" @@ -116,6 +117,7 @@ typedef struct AcpiMiscInfo { uint16_t pvpanic_port; uint16_t battery_port; uint16_t acad_port; + uint16_t button_port; uint16_t applesmc_io_base; } AcpiMiscInfo; =20 @@ -283,6 +285,7 @@ static void acpi_get_misc_info(AcpiMiscInfo *info) info->pvpanic_port =3D pvpanic_port(); info->battery_port =3D battery_port(); info->acad_port =3D acad_port(); + info->button_port =3D button_port(); info->applesmc_io_base =3D applesmc_port(); } =20 @@ -1780,6 +1783,32 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, aml_append(dsdt, method); } =20 + if (misc->button_port) { + Aml *button_state =3D aml_local(0); + + dev =3D aml_device("LID0"); + aml_append(dev, aml_name_decl("_HID", aml_string("PNP0C0D"))); + + aml_append(dev, aml_operation_region("LSTA", AML_SYSTEM_IO, + aml_int(misc->button_port), + BUTTON_LEN)); + field =3D aml_field("LSTA", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE= ); + aml_append(field, aml_named_field("LIDS", 8)); + aml_append(dev, field); + + method =3D aml_method("_LID", 0, AML_NOTSERIALIZED); + aml_append(method, aml_store(aml_name("LIDS"), button_state)); + aml_append(method, aml_return(button_state)); + aml_append(dev, method); + + aml_append(sb_scope, dev); + + /* Status Change */ + method =3D aml_method("\\_GPE._E0B", 0, AML_NOTSERIALIZED); + aml_append(method, aml_notify(aml_name("\\_SB.LID0"), aml_int(0x80= ))); + aml_append(dsdt, method); + } + aml_append(dsdt, sb_scope); =20 /* copy AML table into ACPI tables blob and patch header there */ diff --git a/include/hw/acpi/acpi_dev_interface.h b/include/hw/acpi/acpi_de= v_interface.h index b577a4db07..5ba37a42d2 100644 --- a/include/hw/acpi/acpi_dev_interface.h +++ b/include/hw/acpi/acpi_dev_interface.h @@ -16,6 +16,7 @@ typedef enum { ACPI_POWER_DOWN_STATUS =3D 64, ACPI_BATTERY_CHANGE_STATUS =3D 128, ACPI_AC_ADAPTER_CHANGE_STATUS =3D 1024, + ACPI_BUTTON_CHANGE_STATUS =3D 2048, } AcpiEventStatusBits; =20 #define TYPE_ACPI_DEVICE_IF "acpi-device-interface" diff --git a/include/hw/acpi/button.h b/include/hw/acpi/button.h new file mode 100644 index 0000000000..6da17d7cee --- /dev/null +++ b/include/hw/acpi/button.h @@ -0,0 +1,35 @@ +/* + * QEMU emulated button device. + * + * Copyright (c) 2019 Janus Technologies, Inc. (http://janustech.com) + * + * Authors: + * Leonid Bloch + * Marcel Apfelbaum + * Dmitry Fleytman + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory for details. + * + */ + +#ifndef HW_ACPI_BUTTON_H +#define HW_ACPI_BUTTON_H + +#define TYPE_BUTTON "button" +#define BUTTON_IOPORT_PROP "ioport" +#define BUTTON_PATH_PROP "procfs_path" +#define BUTTON_PROBE_STATE_INTERVAL "probe_interval" + +#define BUTTON_LEN 1 + +static inline uint16_t button_port(void) +{ + Object *o =3D object_resolve_path_type("", TYPE_BUTTON, NULL); + if (!o) { + return 0; + } + return object_property_get_uint(o, BUTTON_IOPORT_PROP, NULL); +} + +#endif --=20 2.30.0