kern/124223: [patch] acpi_battery.c -- Notify user-defined critical level via devd(8)

Pietro Cerutti gahr at FreeBSD.org
Mon Jun 2 23:00:08 UTC 2008


>Number:         124223
>Category:       kern
>Synopsis:       [patch] acpi_battery.c -- Notify user-defined critical level via devd(8)
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Mon Jun 02 23:00:07 UTC 2008
>Closed-Date:
>Last-Modified:
>Originator:     Pietro Cerutti
>Release:        FreeBSD 8.0-CURRENT i386
>Organization:
The FreeBSD Project 
>Environment:


System: FreeBSD 8.0-CURRENT #39: Mon Jun  2 20:33:05 CEST 2008
    root at gahrtop.localhost:/usr/obj/usr/src/sys/MSI1034



>Description:


Critically low battery levels are notified by the ACPI subsystem via the acpi_cmbat.c module.
This prints a line on the console.
The problem with cmbat is that the "critically low level" value is not configurable.

The following patch implements a kernel process within acpi_battery.c.
Two sysctl OIDs are exported in order to control the polling rate and the life % to be considered critical.

When this critical level is reached, acpi_battery.c notifies userland via devd(8), allowing for a devd.conf(5) configuration such as:

notify 10 {
    match "system"      "ACPI";
    match "subsystem"   "Battery";
    match "notify"      "0x80";
    action "logger -p kern.emerg 'WARNING: Low battery!'";
};


>How-To-Repeat:





>Fix:


--- acpi_battery.c.diff begins here ---
Index: acpi_battery.c
===================================================================
RCS file: /home/ncvs/src/sys/dev/acpica/acpi_battery.c,v
retrieving revision 1.26
diff -u -u -r1.26 acpi_battery.c
--- acpi_battery.c	20 Nov 2007 18:35:36 -0000	1.26
+++ acpi_battery.c	23 May 2008 14:35:21 -0000
@@ -31,10 +31,12 @@
 #include "opt_acpi.h"
 #include <sys/param.h>
 #include <sys/kernel.h>
+#include <sys/kthread.h>
 #include <sys/malloc.h>
 #include <sys/bus.h>
 #include <sys/ioccom.h>
 #include <sys/sysctl.h>
+#include <sys/unistd.h>
 
 #include <contrib/dev/acpica/acpi.h>
 #include <dev/acpica/acpivar.h>
@@ -43,6 +45,14 @@
 /* Default seconds before re-sampling the battery state. */
 #define	ACPI_BATTERY_INFO_EXPIRE	5
 
+/* Check for battery low level each 60 seconds */
+#define BATT_POLLRATE 60
+
+/* Default level to notify devd */
+#define BATT_LOWLEVEL 5
+
+#define BATT_NOTIFY_LOWLEVEL    0x80
+
 static int	acpi_batteries_initted;
 static int	acpi_battery_info_expire = ACPI_BATTERY_INFO_EXPIRE;
 static struct	acpi_battinfo	acpi_battery_battinfo;
@@ -56,8 +66,16 @@
 static device_t acpi_battery_find_dev(u_int logical_unit);
 static int acpi_battery_ioctl(u_long cmd, caddr_t addr, void *arg);
 static int acpi_battery_sysctl(SYSCTL_HANDLER_ARGS);
+static int acpi_battery_rate_sysctl(SYSCTL_HANDLER_ARGS);
+static int acpi_battery_crit_sysctl(SYSCTL_HANDLER_ARGS);
 static int acpi_battery_units_sysctl(SYSCTL_HANDLER_ARGS);
 static int acpi_battery_init(void);
+static void acpi_battery_thread(void *);
+
+static struct proc  *acpi_battery_proc;
+static int acpi_battery_pollrate = BATT_POLLRATE;
+static int acpi_battery_lowlevel = BATT_LOWLEVEL;
+static int acpi_battery_previous = -1;
 
 int
 acpi_battery_register(device_t dev)
@@ -69,6 +87,8 @@
     if (!acpi_batteries_initted)
 	error = acpi_battery_init();
     ACPI_SERIAL_END(battery);
+    if(error) return (error);
+    error = kproc_create(acpi_battery_thread, NULL, &acpi_battery_proc, RFHIGHPID, 0, "acpi_battery");
     return (error);
 }
 
@@ -422,6 +442,36 @@
 }
 
 static int
+acpi_battery_rate_sysctl(SYSCTL_HANDLER_ARGS)
+{
+    int error;
+
+    error = sysctl_handle_int(oidp, &acpi_battery_pollrate, 0, req);
+
+    if(error || !req->newptr)
+        return (error);
+    
+    acpi_battery_pollrate = *(int *)req->newptr;
+
+    wakeup(&acpi_battery_proc);
+    return (error);
+}
+
+static int
+acpi_battery_crit_sysctl(SYSCTL_HANDLER_ARGS)
+{
+    int error;
+
+    error = sysctl_handle_int(oidp, &acpi_battery_lowlevel, 0, req);
+    
+    if(error || !req->newptr)
+        return (error);
+
+    acpi_battery_lowlevel = *(int *)req->newptr;
+    return (error);
+}
+
+static int
 acpi_battery_units_sysctl(SYSCTL_HANDLER_ARGS)
 {
     int count, error;
@@ -489,6 +539,16 @@
 	OID_AUTO, "info_expire", CTLFLAG_RW,
 	&acpi_battery_info_expire, 0,
 	"time in seconds until info is refreshed");
+    SYSCTL_ADD_PROC(&acpi_battery_sysctl_ctx,
+	SYSCTL_CHILDREN(acpi_battery_sysctl_tree),
+	OID_AUTO, "polling_rate", CTLTYPE_INT | CTLFLAG_RW,
+	NULL, 0, acpi_battery_rate_sysctl, "I",
+	"polling rate in seconds");
+    SYSCTL_ADD_PROC(&acpi_battery_sysctl_ctx,
+	SYSCTL_CHILDREN(acpi_battery_sysctl_tree),
+	OID_AUTO, "critical_level", CTLTYPE_INT | CTLFLAG_RW,
+	NULL, 0, acpi_battery_crit_sysctl, "I",
+	"critical level in percent");
 
     acpi_batteries_initted = TRUE;
 
@@ -501,3 +561,31 @@
     }
     return (error);
 }
+
+/*
+ * ACPI Battery monitor thread
+ */
+static void
+acpi_battery_thread(void *arg)
+{
+    (void) arg; /* not used */
+    struct acpi_battinfo    battinfo;
+    device_t                dev;
+    ACPI_HANDLE             h;
+
+    if(!(dev = devclass_get_device(devclass_find("acpi"), 0)))
+        return;
+
+    h = acpi_get_handle(dev);
+
+    while(1)
+    {
+        if(!acpi_battery_get_battinfo(NULL, &battinfo))
+        {
+            if(battinfo.cap <= acpi_battery_lowlevel && battinfo.cap < acpi_battery_previous)
+                acpi_UserNotify("Battery", h, BATT_NOTIFY_LOWLEVEL);
+            acpi_battery_previous = battinfo.cap;
+        }
+        tsleep(&acpi_battery_proc, 0, "batpol", hz * acpi_battery_pollrate);
+    }
+}
--- acpi_battery.c.diff ends here ---



>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list