From gahr@FreeBSD.org  Mon Jun  2 22:57:03 2008
Return-Path: <gahr@FreeBSD.org>
Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34])
	by hub.freebsd.org (Postfix) with ESMTP id 2ECD61065675
	for <FreeBSD-gnats-submit@freebsd.org>; Mon,  2 Jun 2008 22:57:03 +0000 (UTC)
	(envelope-from gahr@FreeBSD.org)
Received: from cpanel03.rubas-s03.net (cpanel03.rubas-s03.net [195.182.222.73])
	by mx1.freebsd.org (Postfix) with ESMTP id C86DD8FC1C
	for <FreeBSD-gnats-submit@freebsd.org>; Mon,  2 Jun 2008 22:57:02 +0000 (UTC)
	(envelope-from gahr@FreeBSD.org)
Received: from [213.142.183.219] (helo=gahrtop.localhost)
	by cpanel03.rubas-s03.net with esmtpa (Exim 4.68)
	(envelope-from <gahr@FreeBSD.org>)
	id 1K3IxZ-0001qQ-6o
	for FreeBSD-gnats-submit@freebsd.org; Tue, 03 Jun 2008 00:57:01 +0200
Received: from gahrtop.localhost (localhost [127.0.0.1])
	by gahrtop.localhost (Postfix) with ESMTP id D8CF850841
	for <FreeBSD-gnats-submit@freebsd.org>; Tue,  3 Jun 2008 00:56:53 +0200 (CEST)
Message-Id: <1212447413.32542@gahrtop.localhost>
Date: Tue, 3 Jun 2008 00:56:53 +0200
From: "Pietro Cerutti" <gahr@FreeBSD.org>
To: "FreeBSD gnats submit" <FreeBSD-gnats-submit@freebsd.org>
Subject: [patch] acpi_battery.c -- Notify user-defined critical level via devd(8)
X-Send-Pr-Version: gtk-send-pr 0.4.9 
X-GNATS-Notify:

>Number:         124223
>Category:       kern
>Synopsis:       [acpi] [patch] acpi_battery.c -- Notify user-defined critical level via devd(8)
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-acpi
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Mon Jun 02 23:00:07 UTC 2008
>Closed-Date:    Mon Sep 08 22:15:01 UTC 2008
>Last-Modified:  Mon Sep  8 22:20:02 UTC 2008
>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@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:
Responsible-Changed-From-To: freebsd-bugs->freebsd-acpi 
Responsible-Changed-By: linimon 
Responsible-Changed-When: Tue Jun 3 00:02:33 UTC 2008 
Responsible-Changed-Why:  
Over to maintainer(s). 

http://www.freebsd.org/cgi/query-pr.cgi?pr=124223 

From: Pietro Cerutti <gahr@FreeBSD.org>
To: bug-followup@FreeBSD.org, gahr@FreeBSD.org, freebsd-acpi@freebsd.org
Cc:  
Subject: Re: kern/124223: [acpi] [patch] acpi_battery.c -- Notify user-defined
 critical level via devd(8)
Date: Fri, 05 Sep 2008 16:22:09 +0200

 -----BEGIN PGP SIGNED MESSAGE-----
 Hash: SHA512
 
 POKE!
 
 Anybody interested in reviewing it?
 
 - --
 Pietro Cerutti
 gahr@FreeBSD.org
 
 PGP Public Key:
 http://gahr.ch/pgp
 
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.9 (FreeBSD)
 
 iEYEAREKAAYFAkjBQI8ACgkQwMJqmJVx946CrQCfY7I1sTPwoPte89cP5zXg5j8S
 YNUAnjO41f8uzOPFDRR9XAvcEiHcB9ID
 =ef6A
 -----END PGP SIGNATURE-----

From: Nate Lawson <nate@root.org>
To: Pietro Cerutti <gahr@FreeBSD.org>
Cc: bug-followup@FreeBSD.org, freebsd-acpi@FreeBSD.org
Subject: Re: kern/124223: [acpi] [patch] acpi_battery.c -- Notify  user-defined
 critical level via devd(8)
Date: Fri, 05 Sep 2008 10:10:40 -0700

 There are a few problems with your approach.
 
 Critical status is already reported with a flag when usermode polls for
 the battery status:
 > if (sc->bst.state & ACPI_BATT_STAT_CRITICAL) {
 >     if ((sc->flags & ACPI_BATT_STAT_CRITICAL) == 0) {
 > 	    sc->flags |= ACPI_BATT_STAT_CRITICAL;
 > 	    device_printf(dev, "critically low charge!\n");
 >     }
 > }
 
 Since usermode utilities already poll, they can handle that flag or
 implement their own notion of critical battery level.  Why introduce a
 new kernel thread to do that same polling?
 
 Don't common battery status tools that poll (say, xbatt) have their own
 way to set a critical level?
 
 -Nate
 
 Pietro Cerutti wrote:
 > POKE!
 > 
 > Anybody interested in reviewing it?
 
 -- 
 Nate

From: Pietro Cerutti <gahr@FreeBSD.org>
To: Nate Lawson <nate@root.org>
Cc: bug-followup@FreeBSD.org, freebsd-acpi@FreeBSD.org
Subject: Re: kern/124223: [acpi] [patch] acpi_battery.c -- Notify  user-defined
 critical level via devd(8)
Date: Mon, 08 Sep 2008 23:43:52 +0200

 -----BEGIN PGP SIGNED MESSAGE-----
 Hash: SHA512
 
 Nate Lawson wrote:
 | There are a few problems with your approach.
 |
 | Critical status is already reported with a flag when usermode polls for
 | the battery status:
 |> if (sc->bst.state & ACPI_BATT_STAT_CRITICAL) {
 |>     if ((sc->flags & ACPI_BATT_STAT_CRITICAL) == 0) {
 |> 	    sc->flags |= ACPI_BATT_STAT_CRITICAL;
 |> 	    device_printf(dev, "critically low charge!\n");
 |>     }
 |> }
 
 I agree. Critical level is already checked for in the cmbat module.
 However, reporting is not done in a "standardized" way. My patch would
 just like to add notification through devd.
 
 | Since usermode utilities already poll, they can handle that flag or
 | implement their own notion of critical battery level.  Why introduce a
 | new kernel thread to do that same polling?
 Because you can't configure the notion of "critical level" via cmbat.
 
 |
 | Don't common battery status tools that poll (say, xbatt) have their own
 | way to set a critical level?
 xbatt uses APM and ioctl to get the status of the battery.
 
 I agree that userlevel utilities can e.g., retrieve the remaining
 percent and set the critical level arbitrarily, but I anyway think that
 a configurable level to be seen as critical by the OS could be a useful
 addition.
 
 I'm open to discussions about the best way to implement it (cmbat,
 battery, ...?), if we get to an agreement that this could be a useful
 feature :) Otherwise feel free to close the PR, as I can accept your
 arguments.
 
 |
 | -Nate
 |
 | Pietro Cerutti wrote:
 |> POKE!
 |>
 |> Anybody interested in reviewing it?
 |
 
 
 - --
 Pietro Cerutti
 gahr@FreeBSD.org
 
 PGP Public Key:
 http://gahr.ch/pgp
 
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.9 (FreeBSD)
 
 iEYEAREKAAYFAkjFnJcACgkQwMJqmJVx946mtgCgivQVqG6nrctsffXFdm5HRQfX
 1OUAoL9TXYwwiNfHvnUclKAhVzWcnw9S
 =3q5J
 -----END PGP SIGNATURE-----

From: Nate Lawson <nate@root.org>
To: Pietro Cerutti <gahr@FreeBSD.org>
Cc: bug-followup@FreeBSD.org, freebsd-acpi@FreeBSD.org
Subject: Re: kern/124223: [acpi] [patch] acpi_battery.c -- Notify  user-defined
  critical level via devd(8)
Date: Mon, 08 Sep 2008 15:04:02 -0700

 Pietro Cerutti wrote:
 > Nate Lawson wrote:
 > | There are a few problems with your approach.
 > |
 > | Critical status is already reported with a flag when usermode polls for
 > | the battery status:
 > |> if (sc->bst.state & ACPI_BATT_STAT_CRITICAL) {
 > |>     if ((sc->flags & ACPI_BATT_STAT_CRITICAL) == 0) {
 > |>         sc->flags |= ACPI_BATT_STAT_CRITICAL;
 > |>         device_printf(dev, "critically low charge!\n");
 > |>     }
 > |> }
 > 
 > I agree. Critical level is already checked for in the cmbat module.
 > However, reporting is not done in a "standardized" way. My patch would
 > just like to add notification through devd.
 
 But it doesn't just add notification through devd.  It adds a thread,
 that separately polls for battery state and sends a notify through devd.
  If the user is running any battery app, there's a double poll for the
 same info.
 
 I subscribe to the design approach that where it makes sense to do
 something in usermode, don't do it in kernel mode.  In this case, the IO
 interface is poll-only, and any user app that is running can set its own
 policy for how to deal with the information it gets from polling.
 
 > I agree that userlevel utilities can e.g., retrieve the remaining
 > percent and set the critical level arbitrarily, but I anyway think that
 > a configurable level to be seen as critical by the OS could be a useful
 > addition.
 > 
 > I'm open to discussions about the best way to implement it (cmbat,
 > battery, ...?), if we get to an agreement that this could be a useful
 > feature :) Otherwise feel free to close the PR, as I can accept your
 > arguments.
 
 I think it would be better to work with the third-party apps to support
 configurable levels.  In xbatt:
 
     /* If battery life is short, ring a bell */
     if ((life != APM_STAT_UNKNOWN) && (life < 5) && ((life % 5) == 0)) {
 
 ... and ...
 
       /* set gage color */
         if (displayType == Color) {
             if (life < 3) {
                 gage_color = "red";
             } else if (life < 5) {
                 gage_color = "yellow";
             }
         }
 
 These thresholds could easily be a command-line var or X resource.
 Let's keep this in usermode, where policy belongs.
 
 -- 
 Nate
State-Changed-From-To: open->closed 
State-Changed-By: gahr 
State-Changed-When: Mon Sep 8 22:15:00 UTC 2008 
State-Changed-Why:  
Policy belongs to usermode. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=124223 

From: Pietro Cerutti <gahr@FreeBSD.org>
To: Nate Lawson <nate@root.org>
Cc: bug-followup@FreeBSD.org, freebsd-acpi@FreeBSD.org
Subject: Re: kern/124223: [acpi] [patch] acpi_battery.c -- Notify  user-defined
  critical level via devd(8)
Date: Tue, 09 Sep 2008 00:14:14 +0200

 -----BEGIN PGP SIGNED MESSAGE-----
 Hash: SHA512
 
 Nate Lawson wrote:
 | Pietro Cerutti wrote:
 |> Nate Lawson wrote:
 |> | There are a few problems with your approach.
 |> |
 |> | Critical status is already reported with a flag when usermode polls for
 |> | the battery status:
 |> |> if (sc->bst.state & ACPI_BATT_STAT_CRITICAL) {
 |> |>     if ((sc->flags & ACPI_BATT_STAT_CRITICAL) == 0) {
 |> |>         sc->flags |= ACPI_BATT_STAT_CRITICAL;
 |> |>         device_printf(dev, "critically low charge!\n");
 |> |>     }
 |> |> }
 |>
 |> I agree. Critical level is already checked for in the cmbat module.
 |> However, reporting is not done in a "standardized" way. My patch would
 |> just like to add notification through devd.
 |
 | But it doesn't just add notification through devd.  It adds a thread,
 | that separately polls for battery state and sends a notify through devd.
 |  If the user is running any battery app, there's a double poll for the
 | same info.
 |
 | I subscribe to the design approach that where it makes sense to do
 | something in usermode, don't do it in kernel mode.  In this case, the IO
 | interface is poll-only, and any user app that is running can set its own
 | policy for how to deal with the information it gets from polling.
 
 [snip xbatt-related stuff]
 
 | Let's keep this in usermode, where policy belongs.
 
 That's fine. Thanks for reviewing that!
 
 Best,
 
 - --
 Pietro Cerutti
 gahr@FreeBSD.org
 
 PGP Public Key:
 http://gahr.ch/pgp
 
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.9 (FreeBSD)
 
 iEYEAREKAAYFAkjFo7UACgkQwMJqmJVx945TJQCfTRuG0ZiMSfyIaw0rb/5C1cXY
 E4oAoJdERo/AA7KwGRtYnVEQeUoPo9Az
 =UAwz
 -----END PGP SIGNATURE-----
>Unformatted:
