Implement battery management sysctl. This will provide a set of sysctls
authorkettenis <kettenis@openbsd.org>
Wed, 17 May 2023 22:12:51 +0000 (22:12 +0000)
committerkettenis <kettenis@openbsd.org>
Wed, 17 May 2023 22:12:51 +0000 (22:12 +0000)
to control the charging of laptop batteries:

* hw.battery.chargemode (int)

  -1: force discharge
   0: inhibit charge
   1: auto

In auto mode charging may be controlled by:

* hw.battery.chargestop (int)

  Percentage (0-100) of last full capacity at which the battery should
  stop charging.

* hw.battery.chargestart (int)

  Percentage (0-100) of last full capacity at which the battery should
  start charging.

The idea is that with

  hw.battery.chargemode=1
  hw.battery.chargestop=80
  hw.battery.chargestart=75

the battery would be kept charged within the range between 75% and 80%.

Allowable settings and some details of the behavior may differ between
hardware implementations.

Committing this early to easy testing of further diffs that implement this
functionality in acpithinkpad(4) and aplsmc(4).

ok kn@

sbin/sysctl/sysctl.c
sys/kern/kern_sysctl.c
sys/sys/sysctl.h

index e7ce921..c22bd4b 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: sysctl.c,v 1.258 2021/07/12 15:09:19 beck Exp $       */
+/*     $OpenBSD: sysctl.c,v 1.259 2023/05/17 22:12:51 kettenis Exp $   */
 /*     $NetBSD: sysctl.c,v 1.9 1995/09/30 07:12:50 thorpej Exp $       */
 
 /*
@@ -132,6 +132,7 @@ struct ctlname ddbname[] = CTL_DDB_NAMES;
 struct ctlname audioname[] = CTL_KERN_AUDIO_NAMES;
 struct ctlname videoname[] = CTL_KERN_VIDEO_NAMES;
 struct ctlname witnessname[] = CTL_KERN_WITNESS_NAMES;
+struct ctlname batteryname[] = CTL_HW_BATTERY_NAMES;
 char names[BUFSIZ];
 int lastused;
 
@@ -223,6 +224,7 @@ int sysctl_chipset(char *, char **, int *, int, int *);
 int sysctl_audio(char *, char **, int *, int, int *);
 int sysctl_video(char *, char **, int *, int, int *);
 int sysctl_witness(char *, char **, int *, int, int *);
+int sysctl_battery(char *, char **, int *, int, int *);
 void vfsinit(void);
 
 char *equ = "=";
@@ -558,6 +560,11 @@ parse(char *string, int flags)
                        if (len < 0)
                                return;
                        break;
+               case HW_BATTERY:
+                       len = sysctl_battery(string, &bufp, mib, flags, &type);
+                       if (len < 0)
+                               return;
+                       break;
                case HW_PHYSMEM:
                case HW_USERMEM:
                        /*
@@ -1782,6 +1789,7 @@ struct list tclist = { tcname, KERN_TIMECOUNTER_MAXID };
 struct list audiolist = { audioname, KERN_AUDIO_MAXID };
 struct list videolist = { videoname, KERN_VIDEO_MAXID };
 struct list witnesslist = { witnessname, KERN_WITNESS_MAXID };
+struct list batterylist = { batteryname, HW_BATTERY_MAXID };
 
 /*
  * handle vfs namei cache statistics
@@ -2911,6 +2919,26 @@ sysctl_witness(char *string, char **bufpp, int mib[], int flags, int *typep)
        return (3);
 }
 
+/*
+ * Handle battery support
+ */
+int
+sysctl_battery(char *string, char **bufpp, int mib[], int flags,
+    int *typep)
+{
+       int indx;
+
+       if (*bufpp == NULL) {
+               listall(string, &batterylist);
+               return (-1);
+       }
+       if ((indx = findname(string, "third", bufpp, &batterylist)) == -1)
+               return (-1);
+       mib[2] = indx;
+       *typep = batterylist.list[indx].ctl_type;
+       return (3);
+}
+
 /*
  * Scan a list of names searching for a particular name.
  */
index 1c5adf4..7c19cf3 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: kern_sysctl.c,v 1.412 2023/05/04 09:40:36 mvs Exp $   */
+/*     $OpenBSD: kern_sysctl.c,v 1.413 2023/05/17 22:12:51 kettenis Exp $      */
 /*     $NetBSD: kern_sysctl.c,v 1.17 1996/05/20 17:49:05 mrg Exp $     */
 
 /*-
@@ -143,6 +143,7 @@ int sysctl_audio(int *, u_int, void *, size_t *, void *, size_t);
 int sysctl_video(int *, u_int, void *, size_t *, void *, size_t);
 int sysctl_cpustats(int *, u_int, void *, size_t *, void *, size_t);
 int sysctl_utc_offset(void *, size_t *, void *, size_t);
+int sysctl_hwbattery(int *, u_int, void *, size_t *, void *, size_t);
 
 void fill_file(struct kinfo_file *, struct file *, struct filedesc *, int,
     struct vnode *, struct process *, struct proc *, struct socket *, int);
@@ -689,8 +690,11 @@ hw_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
        extern char machine[], cpu_model[];
        int err, cpuspeed;
 
-       /* all sysctl names at this level except sensors are terminal */
-       if (name[0] != HW_SENSORS && namelen != 1)
+       /*
+        * all sysctl names at this level except sensors and battery
+        * are terminal
+        */
+       if (name[0] != HW_SENSORS && name[0] != HW_BATTERY && namelen != 1)
                return (ENOTDIR);               /* overloaded */
 
        switch (name[0]) {
@@ -776,6 +780,11 @@ hw_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
 #ifdef __HAVE_CPU_TOPOLOGY
        case HW_SMT:
                return (sysctl_hwsmt(oldp, oldlenp, newp, newlen));
+#endif
+#ifndef SMALL_KERNEL
+       case HW_BATTERY:
+               return (sysctl_hwbattery(name + 1, namelen - 1, oldp, oldlenp,
+                   newp, newlen));
 #endif
        default:
                return sysctl_bounded_arr(hw_vars, nitems(hw_vars), name,
@@ -784,6 +793,97 @@ hw_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
        /* NOTREACHED */
 }
 
+#ifndef SMALL_KERNEL
+
+int hw_battery_chargemode;
+int hw_battery_chargestart;
+int hw_battery_chargestop;
+int (*hw_battery_setchargemode)(int);
+int (*hw_battery_setchargestart)(int);
+int (*hw_battery_setchargestop)(int);
+
+int
+sysctl_hwchargemode(void *oldp, size_t *oldlenp, void *newp, size_t newlen)
+{
+       int mode = hw_battery_chargemode;
+       int error;
+
+       if (!hw_battery_setchargemode)
+               return EOPNOTSUPP;
+
+       error = sysctl_int_bounded(oldp, oldlenp, newp, newlen,
+           &mode, -1, 1);
+       if (error)
+               return error;
+
+       if (newp != NULL)
+               error = hw_battery_setchargemode(mode);
+
+       return error;
+}
+
+int
+sysctl_hwchargestart(void *oldp, size_t *oldlenp, void *newp, size_t newlen)
+{
+       int start = hw_battery_chargestart;
+       int error;
+
+       if (!hw_battery_setchargestart)
+               return EOPNOTSUPP;
+
+       error = sysctl_int_bounded(oldp, oldlenp, newp, newlen,
+           &start, 0, 100);
+       if (error)
+               return error;
+
+       if (newp != NULL)
+               error = hw_battery_setchargestart(start);
+
+       return error;
+}
+
+int
+sysctl_hwchargestop(void *oldp, size_t *oldlenp, void *newp, size_t newlen)
+{
+       int stop = hw_battery_chargestop;
+       int error;
+
+       if (!hw_battery_setchargestart)
+               return EOPNOTSUPP;
+
+       error = sysctl_int_bounded(oldp, oldlenp, newp, newlen,
+           &stop, 0, 100);
+       if (error)
+               return error;
+
+       if (newp != NULL)
+               error = hw_battery_setchargestop(stop);
+
+       return error;
+}
+
+int
+sysctl_hwbattery(int *name, u_int namelen, void *oldp, size_t *oldlenp,
+    void *newp, size_t newlen)
+{
+       if (namelen != 1)
+               return (ENOTDIR);
+
+       switch (name[0]) {
+       case HW_BATTERY_CHARGEMODE:
+               return (sysctl_hwchargemode(oldp, oldlenp, newp, newlen));
+       case HW_BATTERY_CHARGESTART:
+               return (sysctl_hwchargestart(oldp, oldlenp, newp, newlen));
+       case HW_BATTERY_CHARGESTOP:
+               return (sysctl_hwchargestop(oldp, oldlenp, newp, newlen));
+       default:
+               return (EOPNOTSUPP);
+       }
+       /* NOTREACHED */
+}
+
+#endif
+
 #ifdef DEBUG_SYSCTL
 /*
  * Debugging related system variables.
index ee79387..083b2ae 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: sysctl.h,v 1.232 2023/01/07 05:24:58 guenther Exp $   */
+/*     $OpenBSD: sysctl.h,v 1.233 2023/05/17 22:12:51 kettenis Exp $   */
 /*     $NetBSD: sysctl.h,v 1.16 1996/04/09 20:55:36 cgd Exp $  */
 
 /*
@@ -948,7 +948,8 @@ struct kinfo_file {
 #define        HW_SMT                  24      /* int: enable SMT/HT/CMT */
 #define        HW_NCPUONLINE           25      /* int: number of cpus being used */
 #define        HW_POWER                26      /* int: machine has wall-power */
-#define        HW_MAXID                27      /* number of valid hw ids */
+#define        HW_BATTERY              27      /* node: battery */
+#define        HW_MAXID                30      /* number of valid hw ids */
 
 #define        CTL_HW_NAMES { \
        { 0, 0 }, \
@@ -978,6 +979,22 @@ struct kinfo_file {
        { "smt", CTLTYPE_INT }, \
        { "ncpuonline", CTLTYPE_INT }, \
        { "power", CTLTYPE_INT }, \
+       { "battery", CTLTYPE_NODE }, \
+}
+
+/*
+ * HW_BATTERY
+ */
+#define        HW_BATTERY_CHARGEMODE   1       /* int: battery charging mode */
+#define        HW_BATTERY_CHARGESTART  2       /* int: battery start charge percent */
+#define        HW_BATTERY_CHARGESTOP   3       /* int: battery stop charge percent */
+#define        HW_BATTERY_MAXID        4
+
+#define CTL_HW_BATTERY_NAMES { \
+       { 0, 0 }, \
+       { "chargemode", CTLTYPE_INT }, \
+       { "chargestart", CTLTYPE_INT }, \
+       { "chargestop", CTLTYPE_INT }, \
 }
 
 /*