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);