Re: /proc/acpi/battery gone again, breaks wmpower

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, Feb 21, 2022 at 5:52 PM Rafael J. Wysocki <rafael@xxxxxxxxxx> wrote:
>
> On Fri, Feb 18, 2022 at 12:17 AM Carl Edquist <edquist@xxxxxxxxxxx> wrote:
> >
> > Hello there Rafael,
> >
> > (I found your contact in the MAINTAINERS file for the ACPI section.
> > Hopefully you are the right person to ask this question, but let me know
> > if there's a better place to ask!)
> >
> > Slackware 15 was released this month, and after upgrading (to linux
> > 5.15.19, according to uname -r), I noticed my wmpower dockapp for the
> > WindowMaker window manager stopped working.  Apparently it relies on
> > /proc/acpi/battery, and does not know about the /sys alternatives.  Maybe
> > it was written before this acpi interface was deprecated.
> >
> > Since wmpower has "just worked" for so long, it appears the only
> > maintenance it's received in a long time has been by the Slackware
> > packagers (slackbuilds.org) to fix compiler issues with more recent
> > versions of gcc.  But people like me definitely still use it! :)
> >
> >
> > I tried searching a bit to see what's going on, and I found an earlier
> > discussion where /proc/acpi/battery had previously been removed, circa
> > linux 3.14, and then re-added, after it was brought up that (possibly
> > among other things) a number of WindowManager dock apps still relied on
> > /proc/acpi/battery:
> >
> >         https://lkml.org/lkml/2014/3/17/393
> >
> >
> > I didn't find a more recent discussion about it getting removed again, but
> > some git-log searching brought me to this commit:
> >
> >
> > commit 8830280a69ddfdbba7fb24d79dce309817783c6a
> > Author: Thomas Renninger <trenn@xxxxxxx>
> > Date:   Wed May 27 17:33:03 2020 +0200
> >
> >      ACPI: procfs: Remove last dirs after being marked deprecated for a decade
> >
> >      This code is outdated and has been deprecated for a long time, so user
> >      space is not expected to rely on it any more on any systems that are
> >      up to date by any reasonable measure.  Remove it.
> >
> >      Signed-off-by: Thomas Renninger <trenn@xxxxxxx>
> >      [ rjw: Subject / changelog ]
> >      Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@xxxxxxxxx>
> >
> >
> > Out of curiousity, I tried reverting this commit against the latest
> > master.  It does not revert cleanly, though it doesn't look like a lot to
> > clean up (conflicts in drivers/acpi/ac.c & battery.c).  Would reverting
> > this commit (assuming conflicts can be fixed) be enough for me to restore
> > the old /proc/acpi/battery interface?
>
> It should be sufficient.
>
> > Or are there likely to be any gotchas there?
> >
> >
> > And, I'm afraid to ask, but is it out of the question to wonder if this,
> > now twice removed, acpi interface may ever see its way back into mainline?
>
> Because we promise that user space won't be broken by kernel changes,
> it needs to be restored to make wmpower work again.
>
> I'd rather not restore all of it, though, just the part relied on by wmpower.

Please check if the attached patch (on top of 5.17-rc5) is sufficient
to restore the wmpower functionality.
From: Rafael J. Wysocki <rafael.j.wysocki@xxxxxxxxx>
Subject: [PATCH] ACPI: battery / AC: Restore the old proc interface

Restore the old proc interface for the ACPI battery and AC drivers,
removed by commit 8830280a69dd ("ACPI: procfs: Remove last dirs after
being marked deprecated for a decade") on the premise that it should
not be used any more, but in fact it is used by the wmpower utility
still shipped in Slackware 15 released this month.

Fixes: 8830280a69dd ("ACPI: procfs: Remove last dirs after being marked deprecated for a decade")
Reported-by: Carl Edquist <edquist@xxxxxxxxxxx>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@xxxxxxxxx>
---
 drivers/acpi/Kconfig   |   17 +++
 drivers/acpi/ac.c      |  108 ++++++++++++++++++++-
 drivers/acpi/battery.c |  252 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 375 insertions(+), 2 deletions(-)

Index: linux-pm/drivers/acpi/Kconfig
===================================================================
--- linux-pm.orig/drivers/acpi/Kconfig
+++ linux-pm/drivers/acpi/Kconfig
@@ -110,6 +110,23 @@ config ACPI_SLEEP
 	depends on ACPI_SYSTEM_POWER_STATES_SUPPORT
 	default y
 
+config ACPI_PROCFS_POWER
+	bool "Deprecated power /proc/acpi directories"
+	depends on X86 && PROC_FS
+	help
+	  For backwards compatibility, this option allows
+	  deprecated power /proc/acpi/ directories to exist, even when
+	  they have been replaced by functions in /sys.
+	  The deprecated directories (and their replacements) include:
+	  /proc/acpi/battery/* (/sys/class/power_supply/*) and
+	  /proc/acpi/ac_adapter/* (sys/class/power_supply/*).
+	  This option has no effect on /proc/acpi/ directories
+	  and functions which do not yet exist in /sys.
+	  This option, together with the proc directories, will be
+	  deleted in the future.
+
+	  Say N to delete power /proc/acpi/ directories that have moved to /sys.
+
 config ACPI_REV_OVERRIDE_POSSIBLE
 	bool "Allow supported ACPI revision to be overridden"
 	depends on X86
Index: linux-pm/drivers/acpi/ac.c
===================================================================
--- linux-pm.orig/drivers/acpi/ac.c
+++ linux-pm/drivers/acpi/ac.c
@@ -15,6 +15,10 @@
 #include <linux/types.h>
 #include <linux/dmi.h>
 #include <linux/delay.h>
+#ifdef CONFIG_ACPI_PROCFS_POWER
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#endif
 #include <linux/platform_device.h>
 #include <linux/power_supply.h>
 #include <linux/acpi.h>
@@ -53,6 +57,12 @@ static int acpi_ac_resume(struct device
 #endif
 static SIMPLE_DEV_PM_OPS(acpi_ac_pm, NULL, acpi_ac_resume);
 
+#ifdef CONFIG_ACPI_PROCFS_POWER
+extern struct proc_dir_entry *acpi_lock_ac_dir(void);
+extern void *acpi_unlock_ac_dir(struct proc_dir_entry *acpi_ac_dir);
+#endif
+
+
 static int ac_sleep_before_get_state_ms;
 static int ac_only;
 
@@ -132,6 +142,74 @@ static enum power_supply_property ac_pro
 	POWER_SUPPLY_PROP_ONLINE,
 };
 
+#ifdef CONFIG_ACPI_PROCFS_POWER
+/* proc I/F */
+static struct proc_dir_entry *acpi_ac_dir;
+
+static int acpi_ac_seq_show(struct seq_file *seq, void *offset)
+{
+	struct acpi_ac *ac = seq->private;
+
+
+	if (!ac)
+		return 0;
+
+	if (acpi_ac_get_state(ac)) {
+		seq_puts(seq, "ERROR: Unable to read AC Adapter state\n");
+		return 0;
+	}
+
+	seq_puts(seq, "state:                   ");
+	switch (ac->state) {
+	case ACPI_AC_STATUS_OFFLINE:
+		seq_puts(seq, "off-line\n");
+		break;
+	case ACPI_AC_STATUS_ONLINE:
+		seq_puts(seq, "on-line\n");
+		break;
+	default:
+		seq_puts(seq, "unknown\n");
+		break;
+	}
+
+	return 0;
+}
+
+static int acpi_ac_add_fs(struct acpi_ac *ac)
+{
+	struct proc_dir_entry *entry = NULL;
+
+	printk(KERN_WARNING PREFIX "Deprecated procfs I/F for AC is loaded,"
+			" please retry with CONFIG_ACPI_PROCFS_POWER cleared\n");
+	if (!acpi_device_dir(ac->device)) {
+		acpi_device_dir(ac->device) =
+			proc_mkdir(acpi_device_bid(ac->device), acpi_ac_dir);
+		if (!acpi_device_dir(ac->device))
+			return -ENODEV;
+	}
+
+	/* 'state' [R] */
+	entry = proc_create_single_data(ACPI_AC_FILE_STATE, S_IRUGO,
+			acpi_device_dir(ac->device), acpi_ac_seq_show, ac);
+	if (!entry)
+		return -ENODEV;
+	return 0;
+}
+
+static int acpi_ac_remove_fs(struct acpi_ac *ac)
+{
+
+	if (acpi_device_dir(ac->device)) {
+		remove_proc_entry(ACPI_AC_FILE_STATE,
+				  acpi_device_dir(ac->device));
+		remove_proc_entry(acpi_device_bid(ac->device), acpi_ac_dir);
+		acpi_device_dir(ac->device) = NULL;
+	}
+
+	return 0;
+}
+#endif
+
 /* Driver Model */
 static void acpi_ac_notify(struct acpi_device *device, u32 event)
 {
@@ -245,6 +323,11 @@ static int acpi_ac_add(struct acpi_devic
 	psy_cfg.drv_data = ac;
 
 	ac->charger_desc.name = acpi_device_bid(device);
+#ifdef CONFIG_ACPI_PROCFS_POWER
+	result = acpi_ac_add_fs(ac);
+	if (result)
+		goto end;
+#endif
 	ac->charger_desc.type = POWER_SUPPLY_TYPE_MAINS;
 	ac->charger_desc.properties = ac_props;
 	ac->charger_desc.num_properties = ARRAY_SIZE(ac_props);
@@ -262,8 +345,12 @@ static int acpi_ac_add(struct acpi_devic
 	ac->battery_nb.notifier_call = acpi_ac_battery_notify;
 	register_acpi_notifier(&ac->battery_nb);
 end:
-	if (result)
+	if (result) {
+#ifdef CONFIG_ACPI_PROCFS_POWER
+		acpi_ac_remove_fs(ac);
+#endif
 		kfree(ac);
+	}
 
 	return result;
 }
@@ -305,6 +392,10 @@ static int acpi_ac_remove(struct acpi_de
 	power_supply_unregister(ac->charger);
 	unregister_acpi_notifier(&ac->battery_nb);
 
+#ifdef CONFIG_ACPI_PROCFS_POWER
+	acpi_ac_remove_fs(ac);
+#endif
+
 	kfree(ac);
 
 	return 0;
@@ -322,9 +413,19 @@ static int __init acpi_ac_init(void)
 
 	dmi_check_system(ac_dmi_table);
 
+#ifdef CONFIG_ACPI_PROCFS_POWER
+	acpi_ac_dir = acpi_lock_ac_dir();
+	if (!acpi_ac_dir)
+		return -ENODEV;
+#endif
+
 	result = acpi_bus_register_driver(&acpi_ac_driver);
-	if (result < 0)
+	if (result < 0) {
+#ifdef CONFIG_ACPI_PROCFS_POWER
+		acpi_unlock_ac_dir(acpi_ac_dir);
+#endif
 		return -ENODEV;
+	}
 
 	return 0;
 }
@@ -332,6 +433,9 @@ static int __init acpi_ac_init(void)
 static void __exit acpi_ac_exit(void)
 {
 	acpi_bus_unregister_driver(&acpi_ac_driver);
+#ifdef CONFIG_ACPI_PROCFS_POWER
+	acpi_unlock_ac_dir(acpi_ac_dir);
+#endif
 }
 module_init(acpi_ac_init);
 module_exit(acpi_ac_exit);
Index: linux-pm/drivers/acpi/battery.c
===================================================================
--- linux-pm.orig/drivers/acpi/battery.c
+++ linux-pm/drivers/acpi/battery.c
@@ -24,6 +24,12 @@
 
 #include <asm/unaligned.h>
 
+#ifdef CONFIG_ACPI_PROCFS_POWER
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#endif
+
 #include <linux/acpi.h>
 #include <linux/power_supply.h>
 
@@ -57,6 +63,11 @@ static unsigned int cache_time = 1000;
 module_param(cache_time, uint, 0644);
 MODULE_PARM_DESC(cache_time, "cache time in milliseconds");
 
+#ifdef CONFIG_ACPI_PROCFS_POWER
+extern struct proc_dir_entry *acpi_lock_battery_dir(void);
+extern void *acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir);
+#endif
+
 static const struct acpi_device_id battery_device_ids[] = {
 	{"PNP0C0A", 0},
 	{"", 0},
@@ -1018,6 +1029,223 @@ static void acpi_battery_refresh(struct
 	sysfs_add_battery(battery);
 }
 
+#ifdef CONFIG_ACPI_PROCFS_POWER
+/* proc I/F */
+static struct proc_dir_entry *acpi_battery_dir;
+
+static const char *acpi_battery_units(const struct acpi_battery *battery)
+{
+	return (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA) ?
+		"mA" : "mW";
+}
+
+static int acpi_battery_info_proc_show(struct seq_file *seq, void *offset)
+{
+	struct acpi_battery *battery = seq->private;
+	int result = acpi_battery_update(battery, false);
+
+	if (result)
+		goto end;
+
+	seq_printf(seq, "present:                 %s\n",
+		   acpi_battery_present(battery) ? "yes" : "no");
+	if (!acpi_battery_present(battery))
+		goto end;
+	if (battery->design_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
+		seq_printf(seq, "design capacity:         unknown\n");
+	else
+		seq_printf(seq, "design capacity:         %d %sh\n",
+			   battery->design_capacity,
+			   acpi_battery_units(battery));
+
+	if (battery->full_charge_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
+		seq_printf(seq, "last full capacity:      unknown\n");
+	else
+		seq_printf(seq, "last full capacity:      %d %sh\n",
+			   battery->full_charge_capacity,
+			   acpi_battery_units(battery));
+
+	seq_printf(seq, "battery technology:      %srechargeable\n",
+		   battery->technology ? "" : "non-");
+
+	if (battery->design_voltage == ACPI_BATTERY_VALUE_UNKNOWN)
+		seq_printf(seq, "design voltage:          unknown\n");
+	else
+		seq_printf(seq, "design voltage:          %d mV\n",
+			   battery->design_voltage);
+	seq_printf(seq, "design capacity warning: %d %sh\n",
+		   battery->design_capacity_warning,
+		   acpi_battery_units(battery));
+	seq_printf(seq, "design capacity low:     %d %sh\n",
+		   battery->design_capacity_low,
+		   acpi_battery_units(battery));
+	seq_printf(seq, "cycle count:		  %i\n", battery->cycle_count);
+	seq_printf(seq, "capacity granularity 1:  %d %sh\n",
+		   battery->capacity_granularity_1,
+		   acpi_battery_units(battery));
+	seq_printf(seq, "capacity granularity 2:  %d %sh\n",
+		   battery->capacity_granularity_2,
+		   acpi_battery_units(battery));
+	seq_printf(seq, "model number:            %s\n", battery->model_number);
+	seq_printf(seq, "serial number:           %s\n", battery->serial_number);
+	seq_printf(seq, "battery type:            %s\n", battery->type);
+	seq_printf(seq, "OEM info:                %s\n", battery->oem_info);
+      end:
+	if (result)
+		seq_printf(seq, "ERROR: Unable to read battery info\n");
+	return result;
+}
+
+static int acpi_battery_state_proc_show(struct seq_file *seq, void *offset)
+{
+	struct acpi_battery *battery = seq->private;
+	int result = acpi_battery_update(battery, false);
+
+	if (result)
+		goto end;
+
+	seq_printf(seq, "present:                 %s\n",
+		   acpi_battery_present(battery) ? "yes" : "no");
+	if (!acpi_battery_present(battery))
+		goto end;
+
+	seq_printf(seq, "capacity state:          %s\n",
+			(battery->state & 0x04) ? "critical" : "ok");
+	if ((battery->state & 0x01) && (battery->state & 0x02))
+		seq_printf(seq,
+			   "charging state:          charging/discharging\n");
+	else if (battery->state & 0x01)
+		seq_printf(seq, "charging state:          discharging\n");
+	else if (battery->state & 0x02)
+		seq_printf(seq, "charging state:          charging\n");
+	else
+		seq_printf(seq, "charging state:          charged\n");
+
+	if (battery->rate_now == ACPI_BATTERY_VALUE_UNKNOWN)
+		seq_printf(seq, "present rate:            unknown\n");
+	else
+		seq_printf(seq, "present rate:            %d %s\n",
+			   battery->rate_now, acpi_battery_units(battery));
+
+	if (battery->capacity_now == ACPI_BATTERY_VALUE_UNKNOWN)
+		seq_printf(seq, "remaining capacity:      unknown\n");
+	else
+		seq_printf(seq, "remaining capacity:      %d %sh\n",
+			   battery->capacity_now, acpi_battery_units(battery));
+	if (battery->voltage_now == ACPI_BATTERY_VALUE_UNKNOWN)
+		seq_printf(seq, "present voltage:         unknown\n");
+	else
+		seq_printf(seq, "present voltage:         %d mV\n",
+			   battery->voltage_now);
+      end:
+	if (result)
+		seq_printf(seq, "ERROR: Unable to read battery state\n");
+
+	return result;
+}
+
+static int acpi_battery_alarm_proc_show(struct seq_file *seq, void *offset)
+{
+	struct acpi_battery *battery = seq->private;
+	int result = acpi_battery_update(battery, false);
+
+	if (result)
+		goto end;
+
+	if (!acpi_battery_present(battery)) {
+		seq_printf(seq, "present:                 no\n");
+		goto end;
+	}
+	seq_printf(seq, "alarm:                   ");
+	if (battery->alarm) {
+		seq_printf(seq, "%u %sh\n", battery->alarm,
+				acpi_battery_units(battery));
+	} else {
+		seq_printf(seq, "unsupported\n");
+	}
+      end:
+	if (result)
+		seq_printf(seq, "ERROR: Unable to read battery alarm\n");
+	return result;
+}
+
+static ssize_t acpi_battery_write_alarm(struct file *file,
+					const char __user * buffer,
+					size_t count, loff_t * ppos)
+{
+	int result = 0;
+	char alarm_string[12] = { '\0' };
+	struct seq_file *m = file->private_data;
+	struct acpi_battery *battery = m->private;
+
+	if (!battery || (count > sizeof(alarm_string) - 1))
+		return -EINVAL;
+	if (!acpi_battery_present(battery)) {
+		result = -ENODEV;
+		goto end;
+	}
+	if (copy_from_user(alarm_string, buffer, count)) {
+		result = -EFAULT;
+		goto end;
+	}
+	alarm_string[count] = '\0';
+	if (kstrtoint(alarm_string, 0, &battery->alarm)) {
+		result = -EINVAL;
+		goto end;
+	}
+	result = acpi_battery_set_alarm(battery);
+      end:
+	if (result)
+		return result;
+	return count;
+}
+
+static int acpi_battery_alarm_proc_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, acpi_battery_alarm_proc_show, PDE_DATA(inode));
+}
+
+static const struct proc_ops acpi_battery_alarm_proc_ops = {
+	.proc_open	= acpi_battery_alarm_proc_open,
+	.proc_read	= seq_read,
+	.proc_write	= acpi_battery_write_alarm,
+	.proc_lseek	= seq_lseek,
+	.proc_release	= single_release,
+};
+
+static int acpi_battery_add_fs(struct acpi_device *device)
+{
+	pr_warn(PREFIX "Deprecated procfs I/F for battery is loaded, please retry with CONFIG_ACPI_PROCFS_POWER cleared\n");
+	if (!acpi_device_dir(device)) {
+		acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
+						     acpi_battery_dir);
+		if (!acpi_device_dir(device))
+			return -ENODEV;
+	}
+
+	if (!proc_create_single_data("info", S_IRUGO, acpi_device_dir(device),
+			acpi_battery_info_proc_show, acpi_driver_data(device)))
+		return -ENODEV;
+	if (!proc_create_single_data("state", S_IRUGO, acpi_device_dir(device),
+			acpi_battery_state_proc_show, acpi_driver_data(device)))
+		return -ENODEV;
+	if (!proc_create_data("alarm", S_IFREG | S_IRUGO | S_IWUSR,
+			acpi_device_dir(device), &acpi_battery_alarm_proc_ops,
+			acpi_driver_data(device)))
+		return -ENODEV;
+	return 0;
+}
+
+static void acpi_battery_remove_fs(struct acpi_device *device)
+{
+	if (!acpi_device_dir(device))
+		return;
+	remove_proc_subtree(acpi_device_bid(device), acpi_battery_dir);
+	acpi_device_dir(device) = NULL;
+}
+
+#endif
+
 /* Driver Interface */
 static void acpi_battery_notify(struct acpi_device *device, u32 event)
 {
@@ -1200,6 +1428,14 @@ static int acpi_battery_add(struct acpi_
 	if (result)
 		goto fail;
 
+#ifdef CONFIG_ACPI_PROCFS_POWER
+	result = acpi_battery_add_fs(device);
+	if (result) {
+		acpi_battery_remove_fs(device);
+		goto fail;
+	}
+#endif
+
 	pr_info("Slot [%s] (battery %s)\n", acpi_device_bid(device),
 		device->status.battery_present ? "present" : "absent");
 
@@ -1227,6 +1463,9 @@ static int acpi_battery_remove(struct ac
 	device_init_wakeup(&device->dev, 0);
 	battery = acpi_driver_data(device);
 	unregister_pm_notifier(&battery->pm_nb);
+#ifdef CONFIG_ACPI_PROCFS_POWER
+	acpi_battery_remove_fs(device);
+#endif
 	sysfs_remove_battery(battery);
 	mutex_destroy(&battery->lock);
 	mutex_destroy(&battery->sysfs_lock);
@@ -1279,7 +1518,16 @@ static void __init acpi_battery_init_asy
 
 	dmi_check_system(bat_dmi_table);
 
+#ifdef CONFIG_ACPI_PROCFS_POWER
+	acpi_battery_dir = acpi_lock_battery_dir();
+	if (!acpi_battery_dir)
+		return;
+#endif
 	result = acpi_bus_register_driver(&acpi_battery_driver);
+#ifdef CONFIG_ACPI_PROCFS_POWER
+	if (result < 0)
+		acpi_unlock_battery_dir(acpi_battery_dir);
+#endif
 	battery_driver_registered = (result == 0);
 }
 
@@ -1299,6 +1547,10 @@ static void __exit acpi_battery_exit(voi
 		acpi_bus_unregister_driver(&acpi_battery_driver);
 		battery_hook_exit();
 	}
+#ifdef CONFIG_ACPI_PROCFS_POWER
+	if (acpi_battery_dir)
+		acpi_unlock_battery_dir(acpi_battery_dir);
+#endif
 }
 
 module_init(acpi_battery_init);

[Index of Archives]     [Linux IBM ACPI]     [Linux Power Management]     [Linux Kernel]     [Linux Laptop]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]

  Powered by Linux