From 22acc011e208b6ae1e8e1d6e3c143e8ae968e310 Mon Sep 17 00:00:00 2001 From: kettenis Date: Wed, 17 May 2023 22:12:51 +0000 Subject: [PATCH] Implement battery management sysctl. This will provide a set of sysctls 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 | 30 +++++++++++- sys/kern/kern_sysctl.c | 106 +++++++++++++++++++++++++++++++++++++++-- sys/sys/sysctl.h | 21 +++++++- 3 files changed, 151 insertions(+), 6 deletions(-) diff --git a/sbin/sysctl/sysctl.c b/sbin/sysctl/sysctl.c index e7ce921994a..c22bd4b94ec 100644 --- a/sbin/sysctl/sysctl.c +++ b/sbin/sysctl/sysctl.c @@ -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. */ diff --git a/sys/kern/kern_sysctl.c b/sys/kern/kern_sysctl.c index 1c5adf4865e..7c19cf363b8 100644 --- a/sys/kern/kern_sysctl.c +++ b/sys/kern/kern_sysctl.c @@ -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. diff --git a/sys/sys/sysctl.h b/sys/sys/sysctl.h index ee7938765d1..083b2ae2121 100644 --- a/sys/sys/sysctl.h +++ b/sys/sys/sysctl.h @@ -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 }, \ } /* -- 2.20.1