15883360bSNate Lawson /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni *
45883360bSNate Lawson * Copyright (c) 2004 Colin Percival
55883360bSNate Lawson * Copyright (c) 2005 Nate Lawson
65883360bSNate Lawson * All rights reserved.
75883360bSNate Lawson *
85883360bSNate Lawson * Redistribution and use in source and binary forms, with or without
95883360bSNate Lawson * modification, are permitted providing that the following conditions
105883360bSNate Lawson * are met:
115883360bSNate Lawson * 1. Redistributions of source code must retain the above copyright
125883360bSNate Lawson * notice, this list of conditions and the following disclaimer.
135883360bSNate Lawson * 2. Redistributions in binary form must reproduce the above copyright
145883360bSNate Lawson * notice, this list of conditions and the following disclaimer in the
155883360bSNate Lawson * documentation and/or other materials provided with the distribution.
165883360bSNate Lawson *
175883360bSNate Lawson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR``AS IS'' AND ANY EXPRESS OR
185883360bSNate Lawson * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
195883360bSNate Lawson * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
205883360bSNate Lawson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
215883360bSNate Lawson * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
225883360bSNate Lawson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
235883360bSNate Lawson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
245883360bSNate Lawson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
255883360bSNate Lawson * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
265883360bSNate Lawson * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
275883360bSNate Lawson * POSSIBILITY OF SUCH DAMAGE.
285883360bSNate Lawson */
295883360bSNate Lawson
3084148fd1SPawel Jakub Dawidek #include <sys/param.h>
3184148fd1SPawel Jakub Dawidek #include <sys/ioctl.h>
32*88a198afSBaptiste Daroussin #include <sys/linker.h>
33*88a198afSBaptiste Daroussin #include <sys/module.h>
3484148fd1SPawel Jakub Dawidek #include <sys/sysctl.h>
3584148fd1SPawel Jakub Dawidek #include <sys/resource.h>
36a1819ab5SNate Lawson #include <sys/socket.h>
372f7a934cSDag-Erling Smørgrav #include <sys/time.h>
38a1819ab5SNate Lawson #include <sys/un.h>
3984148fd1SPawel Jakub Dawidek
40*88a198afSBaptiste Daroussin #include <netlink/netlink.h>
41*88a198afSBaptiste Daroussin #include <netlink/netlink_generic.h>
42*88a198afSBaptiste Daroussin #include <netlink/netlink_snl.h>
43*88a198afSBaptiste Daroussin #include <netlink/netlink_snl_generic.h>
44*88a198afSBaptiste Daroussin #include <netlink/netlink_sysevent.h>
45*88a198afSBaptiste Daroussin
465883360bSNate Lawson #include <err.h>
47f92a4b93SHajimu UMEMOTO #include <errno.h>
485883360bSNate Lawson #include <fcntl.h>
4984148fd1SPawel Jakub Dawidek #include <libutil.h>
50ab19351cSNate Lawson #include <signal.h>
51*88a198afSBaptiste Daroussin #include <stdbool.h>
525883360bSNate Lawson #include <stdio.h>
535883360bSNate Lawson #include <stdlib.h>
545883360bSNate Lawson #include <string.h>
557b3b3683SRobert Millan #include <sysexits.h>
565883360bSNate Lawson #include <unistd.h>
575883360bSNate Lawson
587a377edcSWarner Losh #ifdef __i386__
597a377edcSWarner Losh #define USE_APM
607a377edcSWarner Losh #endif
617a377edcSWarner Losh
622f7a934cSDag-Erling Smørgrav #ifdef USE_APM
635883360bSNate Lawson #include <machine/apm_bios.h>
645f4aa967SMarcel Moolenaar #endif
655883360bSNate Lawson
66dbd31977SAlexander Motin #define DEFAULT_ACTIVE_PERCENT 75
67dbd31977SAlexander Motin #define DEFAULT_IDLE_PERCENT 50
68dbd31977SAlexander Motin #define DEFAULT_POLL_INTERVAL 250 /* Poll interval in milliseconds */
695883360bSNate Lawson
702f7a934cSDag-Erling Smørgrav typedef enum {
715883360bSNate Lawson MODE_MIN,
725883360bSNate Lawson MODE_ADAPTIVE,
73dbd31977SAlexander Motin MODE_HIADAPTIVE,
745883360bSNate Lawson MODE_MAX,
752f7a934cSDag-Erling Smørgrav } modes_t;
765883360bSNate Lawson
772f7a934cSDag-Erling Smørgrav typedef enum {
785883360bSNate Lawson SRC_AC,
795883360bSNate Lawson SRC_BATTERY,
805883360bSNate Lawson SRC_UNKNOWN,
812f7a934cSDag-Erling Smørgrav } power_src_t;
825883360bSNate Lawson
83e120624dSEd Schouten static const char *modes[] = {
845883360bSNate Lawson "AC",
855883360bSNate Lawson "battery",
865883360bSNate Lawson "unknown"
875883360bSNate Lawson };
885883360bSNate Lawson
895883360bSNate Lawson #define ACPIAC "hw.acpi.acline"
90ebcc3763SNathan Whitehorn #define PMUAC "dev.pmu.0.acline"
915883360bSNate Lawson #define APMDEV "/dev/apm"
92a1819ab5SNate Lawson #define DEVDPIPE "/var/run/devd.pipe"
93a1819ab5SNate Lawson #define DEVCTL_MAXBUF 1024
945883360bSNate Lawson
95d9138286SColin Percival static int read_usage_times(int *load, int nonice);
96cf2280acSRebecca Cran static int read_freqs(int *numfreqs, int **freqs, int **power,
97cf2280acSRebecca Cran int minfreq, int maxfreq);
985883360bSNate Lawson static int set_freq(int freq);
9948bd7109SNate Lawson static void acline_init(void);
100*88a198afSBaptiste Daroussin static void acline_read(int rfds);
101*88a198afSBaptiste Daroussin static bool netlink_init(void);
102a1819ab5SNate Lawson static int devd_init(void);
103a1819ab5SNate Lawson static void devd_close(void);
104ab19351cSNate Lawson static void handle_sigs(int sig);
1055883360bSNate Lawson static void parse_mode(char *arg, int *mode, int ch);
1065883360bSNate Lawson static void usage(void);
1075883360bSNate Lawson
1085883360bSNate Lawson /* Sysctl data structures. */
109dbd31977SAlexander Motin static int cp_times_mib[2];
1105883360bSNate Lawson static int freq_mib[4];
1115883360bSNate Lawson static int levels_mib[4];
112ebcc3763SNathan Whitehorn static int acline_mib[4];
113ebcc3763SNathan Whitehorn static size_t acline_mib_len;
1145883360bSNate Lawson
1155883360bSNate Lawson /* Configuration */
1165883360bSNate Lawson static int cpu_running_mark;
1175883360bSNate Lawson static int cpu_idle_mark;
1185883360bSNate Lawson static int poll_ival;
119a1819ab5SNate Lawson static int vflag;
1205883360bSNate Lawson
1212f7a934cSDag-Erling Smørgrav static volatile sig_atomic_t exit_requested;
1222f7a934cSDag-Erling Smørgrav static power_src_t acline_status;
1235c81ba5aSAndriy Voskoboinyk typedef enum {
1242f7a934cSDag-Erling Smørgrav ac_none,
125ebcc3763SNathan Whitehorn ac_sysctl,
1262f7a934cSDag-Erling Smørgrav ac_acpi_devd,
1272f7a934cSDag-Erling Smørgrav #ifdef USE_APM
1282f7a934cSDag-Erling Smørgrav ac_apm,
1292f7a934cSDag-Erling Smørgrav #endif
130*88a198afSBaptiste Daroussin ac_acpi_netlink,
1315c81ba5aSAndriy Voskoboinyk } acline_mode_t;
1325c81ba5aSAndriy Voskoboinyk static acline_mode_t acline_mode;
1335c81ba5aSAndriy Voskoboinyk static acline_mode_t acline_mode_user = ac_none;
1342f7a934cSDag-Erling Smørgrav #ifdef USE_APM
1352f7a934cSDag-Erling Smørgrav static int apm_fd = -1;
1362f7a934cSDag-Erling Smørgrav #endif
1372f7a934cSDag-Erling Smørgrav static int devd_pipe = -1;
138*88a198afSBaptiste Daroussin static bool try_netlink = true;
139*88a198afSBaptiste Daroussin static struct snl_state ss;
1402f7a934cSDag-Erling Smørgrav
1412f7a934cSDag-Erling Smørgrav #define DEVD_RETRY_INTERVAL 60 /* seconds */
1422f7a934cSDag-Erling Smørgrav static struct timeval tried_devd;
14348bd7109SNate Lawson
1440e2a18e6SAlexander Motin /*
1450e2a18e6SAlexander Motin * This function returns summary load of all CPUs. It was made so
1460e2a18e6SAlexander Motin * intentionally to not reduce performance in scenarios when several
1470e2a18e6SAlexander Motin * threads are processing requests as a pipeline -- running one at
148d9138286SColin Percival * a time on different CPUs and waiting for each other. If nonice
149d9138286SColin Percival * is nonzero, only user+sys+intr time will be counted as load; any
150d9138286SColin Percival * nice time will be treated as if idle.
1510e2a18e6SAlexander Motin */
1525883360bSNate Lawson static int
read_usage_times(int * load,int nonice)153d9138286SColin Percival read_usage_times(int *load, int nonice)
1545883360bSNate Lawson {
155dbd31977SAlexander Motin static long *cp_times = NULL, *cp_times_old = NULL;
156dbd31977SAlexander Motin static int ncpus = 0;
157dbd31977SAlexander Motin size_t cp_times_len;
158d9138286SColin Percival int error, cpu, i, total, excl;
1595883360bSNate Lawson
160dbd31977SAlexander Motin if (cp_times == NULL) {
161dbd31977SAlexander Motin cp_times_len = 0;
162dbd31977SAlexander Motin error = sysctl(cp_times_mib, 2, NULL, &cp_times_len, NULL, 0);
1635883360bSNate Lawson if (error)
1645883360bSNate Lawson return (error);
165dbd31977SAlexander Motin if ((cp_times = malloc(cp_times_len)) == NULL)
166dbd31977SAlexander Motin return (errno);
167dbd31977SAlexander Motin if ((cp_times_old = malloc(cp_times_len)) == NULL) {
168dbd31977SAlexander Motin free(cp_times);
169dbd31977SAlexander Motin cp_times = NULL;
170dbd31977SAlexander Motin return (errno);
171dbd31977SAlexander Motin }
172dbd31977SAlexander Motin ncpus = cp_times_len / (sizeof(long) * CPUSTATES);
173dbd31977SAlexander Motin }
1745883360bSNate Lawson
175dbd31977SAlexander Motin cp_times_len = sizeof(long) * CPUSTATES * ncpus;
176dbd31977SAlexander Motin error = sysctl(cp_times_mib, 2, cp_times, &cp_times_len, NULL, 0);
177dbd31977SAlexander Motin if (error)
178dbd31977SAlexander Motin return (error);
1795883360bSNate Lawson
180dbd31977SAlexander Motin if (load) {
181dbd31977SAlexander Motin *load = 0;
182dbd31977SAlexander Motin for (cpu = 0; cpu < ncpus; cpu++) {
183dbd31977SAlexander Motin total = 0;
184dbd31977SAlexander Motin for (i = 0; i < CPUSTATES; i++) {
185dbd31977SAlexander Motin total += cp_times[cpu * CPUSTATES + i] -
186dbd31977SAlexander Motin cp_times_old[cpu * CPUSTATES + i];
187dbd31977SAlexander Motin }
188dbd31977SAlexander Motin if (total == 0)
189dbd31977SAlexander Motin continue;
190d9138286SColin Percival excl = cp_times[cpu * CPUSTATES + CP_IDLE] -
191d9138286SColin Percival cp_times_old[cpu * CPUSTATES + CP_IDLE];
192d9138286SColin Percival if (nonice)
193d9138286SColin Percival excl += cp_times[cpu * CPUSTATES + CP_NICE] -
194d9138286SColin Percival cp_times_old[cpu * CPUSTATES + CP_NICE];
195d9138286SColin Percival *load += 100 - excl * 100 / total;
196dbd31977SAlexander Motin }
197dbd31977SAlexander Motin }
198dbd31977SAlexander Motin
199dbd31977SAlexander Motin memcpy(cp_times_old, cp_times, cp_times_len);
2005883360bSNate Lawson
2015883360bSNate Lawson return (0);
2025883360bSNate Lawson }
2035883360bSNate Lawson
2045883360bSNate Lawson static int
read_freqs(int * numfreqs,int ** freqs,int ** power,int minfreq,int maxfreq)205cf2280acSRebecca Cran read_freqs(int *numfreqs, int **freqs, int **power, int minfreq, int maxfreq)
2065883360bSNate Lawson {
2075883360bSNate Lawson char *freqstr, *p, *q;
208cf2280acSRebecca Cran int i, j;
2095883360bSNate Lawson size_t len = 0;
2105883360bSNate Lawson
2115883360bSNate Lawson if (sysctl(levels_mib, 4, NULL, &len, NULL, 0))
2125883360bSNate Lawson return (-1);
2135883360bSNate Lawson if ((freqstr = malloc(len)) == NULL)
2145883360bSNate Lawson return (-1);
2152a73387fSMark Johnston if (sysctl(levels_mib, 4, freqstr, &len, NULL, 0)) {
2162a73387fSMark Johnston free(freqstr);
2175883360bSNate Lawson return (-1);
2182a73387fSMark Johnston }
2195883360bSNate Lawson
2205883360bSNate Lawson *numfreqs = 1;
2215883360bSNate Lawson for (p = freqstr; *p != '\0'; p++)
2225883360bSNate Lawson if (*p == ' ')
2235883360bSNate Lawson (*numfreqs)++;
2245883360bSNate Lawson
2255883360bSNate Lawson if ((*freqs = malloc(*numfreqs * sizeof(int))) == NULL) {
2265883360bSNate Lawson free(freqstr);
2275883360bSNate Lawson return (-1);
2285883360bSNate Lawson }
229ab19351cSNate Lawson if ((*power = malloc(*numfreqs * sizeof(int))) == NULL) {
230ab19351cSNate Lawson free(freqstr);
231ab19351cSNate Lawson free(*freqs);
232ab19351cSNate Lawson return (-1);
233ab19351cSNate Lawson }
234cf2280acSRebecca Cran for (i = 0, j = 0, p = freqstr; i < *numfreqs; i++) {
2355883360bSNate Lawson q = strchr(p, ' ');
2365883360bSNate Lawson if (q != NULL)
2375883360bSNate Lawson *q = '\0';
238cf2280acSRebecca Cran if (sscanf(p, "%d/%d", &(*freqs)[j], &(*power)[i]) != 2) {
2395883360bSNate Lawson free(freqstr);
2405883360bSNate Lawson free(*freqs);
241ab19351cSNate Lawson free(*power);
2425883360bSNate Lawson return (-1);
2435883360bSNate Lawson }
244cf2280acSRebecca Cran if (((*freqs)[j] >= minfreq || minfreq == -1) &&
245cf2280acSRebecca Cran ((*freqs)[j] <= maxfreq || maxfreq == -1))
246cf2280acSRebecca Cran j++;
2475883360bSNate Lawson p = q + 1;
2485883360bSNate Lawson }
2495883360bSNate Lawson
250cf2280acSRebecca Cran *numfreqs = j;
251cf2280acSRebecca Cran if ((*freqs = realloc(*freqs, *numfreqs * sizeof(int))) == NULL) {
252cf2280acSRebecca Cran free(freqstr);
253cf2280acSRebecca Cran free(*freqs);
254cf2280acSRebecca Cran free(*power);
255cf2280acSRebecca Cran return (-1);
256cf2280acSRebecca Cran }
257cf2280acSRebecca Cran
2585883360bSNate Lawson free(freqstr);
2595883360bSNate Lawson return (0);
2605883360bSNate Lawson }
2615883360bSNate Lawson
2625883360bSNate Lawson static int
get_freq(void)263dbd31977SAlexander Motin get_freq(void)
264dbd31977SAlexander Motin {
265dbd31977SAlexander Motin size_t len;
266dbd31977SAlexander Motin int curfreq;
267dbd31977SAlexander Motin
268dbd31977SAlexander Motin len = sizeof(curfreq);
269dbd31977SAlexander Motin if (sysctl(freq_mib, 4, &curfreq, &len, NULL, 0) != 0) {
270dbd31977SAlexander Motin if (vflag)
271dbd31977SAlexander Motin warn("error reading current CPU frequency");
272dbd31977SAlexander Motin curfreq = 0;
273dbd31977SAlexander Motin }
274dbd31977SAlexander Motin return (curfreq);
275dbd31977SAlexander Motin }
276dbd31977SAlexander Motin
277dbd31977SAlexander Motin static int
set_freq(int freq)2785883360bSNate Lawson set_freq(int freq)
2795883360bSNate Lawson {
2805883360bSNate Lawson
281f92a4b93SHajimu UMEMOTO if (sysctl(freq_mib, 4, NULL, NULL, &freq, sizeof(freq))) {
282f92a4b93SHajimu UMEMOTO if (errno != EPERM)
2835883360bSNate Lawson return (-1);
284f92a4b93SHajimu UMEMOTO }
2855883360bSNate Lawson
2865883360bSNate Lawson return (0);
2875883360bSNate Lawson }
2885883360bSNate Lawson
289dbd31977SAlexander Motin static int
get_freq_id(int freq,int * freqs,int numfreqs)290dbd31977SAlexander Motin get_freq_id(int freq, int *freqs, int numfreqs)
291dbd31977SAlexander Motin {
292dbd31977SAlexander Motin int i = 1;
293dbd31977SAlexander Motin
294dbd31977SAlexander Motin while (i < numfreqs) {
295dbd31977SAlexander Motin if (freqs[i] < freq)
296dbd31977SAlexander Motin break;
297dbd31977SAlexander Motin i++;
298dbd31977SAlexander Motin }
299dbd31977SAlexander Motin return (i - 1);
300dbd31977SAlexander Motin }
301dbd31977SAlexander Motin
30248bd7109SNate Lawson /*
30348bd7109SNate Lawson * Try to use ACPI to find the AC line status. If this fails, fall back
3042f7a934cSDag-Erling Smørgrav * to APM. If nothing succeeds, we'll just run in default mode.
30548bd7109SNate Lawson */
30648bd7109SNate Lawson static void
acline_init(void)30710bc3a7fSEd Schouten acline_init(void)
30848bd7109SNate Lawson {
3095c81ba5aSAndriy Voskoboinyk int skip_source_check;
3105c81ba5aSAndriy Voskoboinyk
311ebcc3763SNathan Whitehorn acline_mib_len = 4;
312a051bcb1SChristian Brueffer acline_status = SRC_UNKNOWN;
3135c81ba5aSAndriy Voskoboinyk skip_source_check = (acline_mode_user == ac_none ||
3145c81ba5aSAndriy Voskoboinyk acline_mode_user == ac_acpi_devd);
31548bd7109SNate Lawson
3165c81ba5aSAndriy Voskoboinyk if ((skip_source_check || acline_mode_user == ac_sysctl) &&
3175c81ba5aSAndriy Voskoboinyk sysctlnametomib(ACPIAC, acline_mib, &acline_mib_len) == 0) {
318ebcc3763SNathan Whitehorn acline_mode = ac_sysctl;
3192f7a934cSDag-Erling Smørgrav if (vflag)
3202f7a934cSDag-Erling Smørgrav warnx("using sysctl for AC line status");
32148c73156SEitan Adler #ifdef __powerpc__
3225c81ba5aSAndriy Voskoboinyk } else if ((skip_source_check || acline_mode_user == ac_sysctl) &&
3235c81ba5aSAndriy Voskoboinyk sysctlnametomib(PMUAC, acline_mib, &acline_mib_len) == 0) {
324ebcc3763SNathan Whitehorn acline_mode = ac_sysctl;
325ebcc3763SNathan Whitehorn if (vflag)
326ebcc3763SNathan Whitehorn warnx("using sysctl for AC line status");
327ebcc3763SNathan Whitehorn #endif
3282f7a934cSDag-Erling Smørgrav #ifdef USE_APM
3295c81ba5aSAndriy Voskoboinyk } else if ((skip_source_check || acline_mode_user == ac_apm) &&
3305c81ba5aSAndriy Voskoboinyk (apm_fd = open(APMDEV, O_RDONLY)) >= 0) {
3312f7a934cSDag-Erling Smørgrav if (vflag)
3322f7a934cSDag-Erling Smørgrav warnx("using APM for AC line status");
3332f7a934cSDag-Erling Smørgrav acline_mode = ac_apm;
3342f7a934cSDag-Erling Smørgrav #endif
33548bd7109SNate Lawson } else {
3362f7a934cSDag-Erling Smørgrav warnx("unable to determine AC line status");
3372f7a934cSDag-Erling Smørgrav acline_mode = ac_none;
33848bd7109SNate Lawson }
33948bd7109SNate Lawson }
34048bd7109SNate Lawson
341*88a198afSBaptiste Daroussin struct nlevent {
342*88a198afSBaptiste Daroussin const char *name;
343*88a198afSBaptiste Daroussin const char *subsystem;
344*88a198afSBaptiste Daroussin const char *type;
345*88a198afSBaptiste Daroussin const char *data;
346*88a198afSBaptiste Daroussin };
347*88a198afSBaptiste Daroussin #define _OUT(_field) offsetof(struct nlevent, _field)
348*88a198afSBaptiste Daroussin static struct snl_attr_parser ap_nlevent_get[] = {
349*88a198afSBaptiste Daroussin { .type = NLSE_ATTR_SYSTEM, .off = _OUT(name), .cb = snl_attr_get_string },
350*88a198afSBaptiste Daroussin { .type = NLSE_ATTR_SUBSYSTEM, .off = _OUT(subsystem), .cb = snl_attr_get_string },
351*88a198afSBaptiste Daroussin { .type = NLSE_ATTR_TYPE, .off = _OUT(type), .cb = snl_attr_get_string },
352*88a198afSBaptiste Daroussin { .type = NLSE_ATTR_DATA, .off = _OUT(data), .cb = snl_attr_get_string },
353*88a198afSBaptiste Daroussin };
354*88a198afSBaptiste Daroussin #undef _OUT
355*88a198afSBaptiste Daroussin
356*88a198afSBaptiste Daroussin SNL_DECLARE_GENL_PARSER(nlevent_get_parser, ap_nlevent_get);
357*88a198afSBaptiste Daroussin
3582f7a934cSDag-Erling Smørgrav static void
acline_read(int rfds)359*88a198afSBaptiste Daroussin acline_read(int rfds)
36048bd7109SNate Lawson {
361*88a198afSBaptiste Daroussin if (acline_mode == ac_acpi_netlink) {
362*88a198afSBaptiste Daroussin struct nlmsghdr *hdr;
363*88a198afSBaptiste Daroussin struct nlevent ne;
364*88a198afSBaptiste Daroussin char *ptr;
365*88a198afSBaptiste Daroussin int notify;
366*88a198afSBaptiste Daroussin
367*88a198afSBaptiste Daroussin if (rfds == 0)
368*88a198afSBaptiste Daroussin return;
369*88a198afSBaptiste Daroussin hdr = snl_read_message(&ss);
370*88a198afSBaptiste Daroussin if (hdr != NULL && hdr->nlmsg_type != NLMSG_ERROR) {
371*88a198afSBaptiste Daroussin memset(&ne, 0, sizeof(ne));
372*88a198afSBaptiste Daroussin if (!snl_parse_nlmsg(&ss, hdr, &nlevent_get_parser, &ne))
373*88a198afSBaptiste Daroussin return;
374*88a198afSBaptiste Daroussin if (strcmp(ne.subsystem, "ACAD") != 0)
375*88a198afSBaptiste Daroussin return;
376*88a198afSBaptiste Daroussin if ((ptr = strstr(ne.data, "notify=")) != NULL &&
377*88a198afSBaptiste Daroussin sscanf(ptr, "notify=%x", ¬ify) == 1)
378*88a198afSBaptiste Daroussin acline_status = (notify ? SRC_AC : SRC_BATTERY);
379*88a198afSBaptiste Daroussin }
380*88a198afSBaptiste Daroussin return;
381*88a198afSBaptiste Daroussin
382*88a198afSBaptiste Daroussin }
3832f7a934cSDag-Erling Smørgrav if (acline_mode == ac_acpi_devd) {
3842f7a934cSDag-Erling Smørgrav char buf[DEVCTL_MAXBUF], *ptr;
3852f7a934cSDag-Erling Smørgrav ssize_t rlen;
3862f7a934cSDag-Erling Smørgrav int notify;
3872f7a934cSDag-Erling Smørgrav
3882f7a934cSDag-Erling Smørgrav rlen = read(devd_pipe, buf, sizeof(buf));
3892f7a934cSDag-Erling Smørgrav if (rlen == 0 || (rlen < 0 && errno != EWOULDBLOCK)) {
3902f7a934cSDag-Erling Smørgrav if (vflag)
3912f7a934cSDag-Erling Smørgrav warnx("lost devd connection, switching to sysctl");
3922f7a934cSDag-Erling Smørgrav devd_close();
393ebcc3763SNathan Whitehorn acline_mode = ac_sysctl;
3942f7a934cSDag-Erling Smørgrav /* FALLTHROUGH */
3952f7a934cSDag-Erling Smørgrav }
3962f7a934cSDag-Erling Smørgrav if (rlen > 0 &&
3972f7a934cSDag-Erling Smørgrav (ptr = strstr(buf, "system=ACPI")) != NULL &&
3982f7a934cSDag-Erling Smørgrav (ptr = strstr(ptr, "subsystem=ACAD")) != NULL &&
3992f7a934cSDag-Erling Smørgrav (ptr = strstr(ptr, "notify=")) != NULL &&
4002f7a934cSDag-Erling Smørgrav sscanf(ptr, "notify=%x", ¬ify) == 1)
4012f7a934cSDag-Erling Smørgrav acline_status = (notify ? SRC_AC : SRC_BATTERY);
4022f7a934cSDag-Erling Smørgrav }
403ebcc3763SNathan Whitehorn if (acline_mode == ac_sysctl) {
404a1819ab5SNate Lawson int acline;
40548bd7109SNate Lawson size_t len;
40648bd7109SNate Lawson
407a1819ab5SNate Lawson len = sizeof(acline);
408ebcc3763SNathan Whitehorn if (sysctl(acline_mib, acline_mib_len, &acline, &len,
409ebcc3763SNathan Whitehorn NULL, 0) == 0)
4102f7a934cSDag-Erling Smørgrav acline_status = (acline ? SRC_AC : SRC_BATTERY);
4112f7a934cSDag-Erling Smørgrav else
4122f7a934cSDag-Erling Smørgrav acline_status = SRC_UNKNOWN;
4132f7a934cSDag-Erling Smørgrav }
4142f7a934cSDag-Erling Smørgrav #ifdef USE_APM
4152f7a934cSDag-Erling Smørgrav if (acline_mode == ac_apm) {
4162f7a934cSDag-Erling Smørgrav struct apm_info info;
417a1819ab5SNate Lawson
4182f7a934cSDag-Erling Smørgrav if (ioctl(apm_fd, APMIO_GETINFO, &info) == 0) {
4192f7a934cSDag-Erling Smørgrav acline_status = (info.ai_acline ? SRC_AC : SRC_BATTERY);
4202f7a934cSDag-Erling Smørgrav } else {
4212f7a934cSDag-Erling Smørgrav close(apm_fd);
4222f7a934cSDag-Erling Smørgrav apm_fd = -1;
4232f7a934cSDag-Erling Smørgrav acline_mode = ac_none;
4242f7a934cSDag-Erling Smørgrav acline_status = SRC_UNKNOWN;
4252f7a934cSDag-Erling Smørgrav }
4262f7a934cSDag-Erling Smørgrav }
42748bd7109SNate Lawson #endif
4282f7a934cSDag-Erling Smørgrav /* try to (re)connect to devd */
4295c81ba5aSAndriy Voskoboinyk #ifdef USE_APM
4305c81ba5aSAndriy Voskoboinyk if ((acline_mode == ac_sysctl &&
4315c81ba5aSAndriy Voskoboinyk (acline_mode_user == ac_none ||
4325c81ba5aSAndriy Voskoboinyk acline_mode_user == ac_acpi_devd)) ||
4335c81ba5aSAndriy Voskoboinyk (acline_mode == ac_apm &&
4345c81ba5aSAndriy Voskoboinyk acline_mode_user == ac_acpi_devd)) {
4355c81ba5aSAndriy Voskoboinyk #else
4365c81ba5aSAndriy Voskoboinyk if (acline_mode == ac_sysctl &&
4375c81ba5aSAndriy Voskoboinyk (acline_mode_user == ac_none ||
438*88a198afSBaptiste Daroussin acline_mode_user == ac_acpi_devd ||
439*88a198afSBaptiste Daroussin acline_mode_user == ac_acpi_netlink)) {
4405c81ba5aSAndriy Voskoboinyk #endif
4412f7a934cSDag-Erling Smørgrav struct timeval now;
44248bd7109SNate Lawson
443*88a198afSBaptiste Daroussin if (acline_mode_user != ac_acpi_devd && try_netlink) {
444*88a198afSBaptiste Daroussin try_netlink = false; /* only try once */
445*88a198afSBaptiste Daroussin if (netlink_init()) {
446*88a198afSBaptiste Daroussin if (vflag)
447*88a198afSBaptiste Daroussin warnx("using netlink for AC line status");
448*88a198afSBaptiste Daroussin acline_mode = ac_acpi_netlink;
449*88a198afSBaptiste Daroussin }
450*88a198afSBaptiste Daroussin return;
451*88a198afSBaptiste Daroussin }
4522f7a934cSDag-Erling Smørgrav gettimeofday(&now, NULL);
4532f7a934cSDag-Erling Smørgrav if (now.tv_sec > tried_devd.tv_sec + DEVD_RETRY_INTERVAL) {
4542f7a934cSDag-Erling Smørgrav if (devd_init() >= 0) {
4552f7a934cSDag-Erling Smørgrav if (vflag)
4562f7a934cSDag-Erling Smørgrav warnx("using devd for AC line status");
4572f7a934cSDag-Erling Smørgrav acline_mode = ac_acpi_devd;
4582f7a934cSDag-Erling Smørgrav }
4592f7a934cSDag-Erling Smørgrav tried_devd = now;
4602f7a934cSDag-Erling Smørgrav }
4612f7a934cSDag-Erling Smørgrav }
46248bd7109SNate Lawson }
46348bd7109SNate Lawson
464*88a198afSBaptiste Daroussin bool
465*88a198afSBaptiste Daroussin netlink_init(void)
466*88a198afSBaptiste Daroussin {
467*88a198afSBaptiste Daroussin struct _getfamily_attrs attrs;
468*88a198afSBaptiste Daroussin
469*88a198afSBaptiste Daroussin if (modfind("nlsysevent") < 0)
470*88a198afSBaptiste Daroussin kldload("nlsysevent");
471*88a198afSBaptiste Daroussin if (modfind("nlsysevent") < 0)
472*88a198afSBaptiste Daroussin return (false);
473*88a198afSBaptiste Daroussin
474*88a198afSBaptiste Daroussin if (!snl_init(&ss, NETLINK_GENERIC))
475*88a198afSBaptiste Daroussin return (false);
476*88a198afSBaptiste Daroussin
477*88a198afSBaptiste Daroussin if (!snl_get_genl_family_info(&ss, "nlsysevent", &attrs))
478*88a198afSBaptiste Daroussin return (false);
479*88a198afSBaptiste Daroussin
480*88a198afSBaptiste Daroussin for (unsigned int i = 0; i < attrs.mcast_groups.num_groups; i++) {
481*88a198afSBaptiste Daroussin if (strcmp(attrs.mcast_groups.groups[i]->mcast_grp_name,
482*88a198afSBaptiste Daroussin "ACPI") == 0) {
483*88a198afSBaptiste Daroussin if (setsockopt(ss.fd, SOL_NETLINK,
484*88a198afSBaptiste Daroussin NETLINK_ADD_MEMBERSHIP,
485*88a198afSBaptiste Daroussin &attrs.mcast_groups.groups[i]->mcast_grp_id,
486*88a198afSBaptiste Daroussin sizeof(attrs.mcast_groups.groups[i]->mcast_grp_id))
487*88a198afSBaptiste Daroussin == -1) {
488*88a198afSBaptiste Daroussin warnx("Cannot subscribe to \"ACPI\"");
489*88a198afSBaptiste Daroussin return (false);
490*88a198afSBaptiste Daroussin }
491*88a198afSBaptiste Daroussin }
492*88a198afSBaptiste Daroussin }
493*88a198afSBaptiste Daroussin return (true);
494*88a198afSBaptiste Daroussin }
495*88a198afSBaptiste Daroussin
496a1819ab5SNate Lawson static int
497a1819ab5SNate Lawson devd_init(void)
498a1819ab5SNate Lawson {
499a1819ab5SNate Lawson struct sockaddr_un devd_addr;
500a1819ab5SNate Lawson
501a1819ab5SNate Lawson bzero(&devd_addr, sizeof(devd_addr));
50279477f3cSBaptiste Daroussin if ((devd_pipe = socket(PF_LOCAL, SOCK_STREAM|SOCK_NONBLOCK, 0)) < 0) {
503a1819ab5SNate Lawson if (vflag)
5042f7a934cSDag-Erling Smørgrav warn("%s(): socket()", __func__);
505a1819ab5SNate Lawson return (-1);
506a1819ab5SNate Lawson }
507a1819ab5SNate Lawson
508a1819ab5SNate Lawson devd_addr.sun_family = PF_LOCAL;
509a1819ab5SNate Lawson strlcpy(devd_addr.sun_path, DEVDPIPE, sizeof(devd_addr.sun_path));
5102f7a934cSDag-Erling Smørgrav if (connect(devd_pipe, (struct sockaddr *)&devd_addr,
511a1819ab5SNate Lawson sizeof(devd_addr)) == -1) {
5122f7a934cSDag-Erling Smørgrav if (vflag)
5132f7a934cSDag-Erling Smørgrav warn("%s(): connect()", __func__);
5142f7a934cSDag-Erling Smørgrav close(devd_pipe);
5152f7a934cSDag-Erling Smørgrav devd_pipe = -1;
516a1819ab5SNate Lawson return (-1);
517a1819ab5SNate Lawson }
518a1819ab5SNate Lawson
5192f7a934cSDag-Erling Smørgrav return (devd_pipe);
520a1819ab5SNate Lawson }
521a1819ab5SNate Lawson
522a1819ab5SNate Lawson static void
523a1819ab5SNate Lawson devd_close(void)
524a1819ab5SNate Lawson {
525a1819ab5SNate Lawson
526a1819ab5SNate Lawson close(devd_pipe);
527a1819ab5SNate Lawson devd_pipe = -1;
52848bd7109SNate Lawson }
52948bd7109SNate Lawson
5305883360bSNate Lawson static void
5315883360bSNate Lawson parse_mode(char *arg, int *mode, int ch)
5325883360bSNate Lawson {
5335883360bSNate Lawson
534db20dc4dSDag-Erling Smørgrav if (strcmp(arg, "minimum") == 0 || strcmp(arg, "min") == 0)
5355883360bSNate Lawson *mode = MODE_MIN;
536db20dc4dSDag-Erling Smørgrav else if (strcmp(arg, "maximum") == 0 || strcmp(arg, "max") == 0)
5375883360bSNate Lawson *mode = MODE_MAX;
538dc70a966SDavid E. O'Brien else if (strcmp(arg, "adaptive") == 0 || strcmp(arg, "adp") == 0)
5395883360bSNate Lawson *mode = MODE_ADAPTIVE;
540dbd31977SAlexander Motin else if (strcmp(arg, "hiadaptive") == 0 || strcmp(arg, "hadp") == 0)
541dbd31977SAlexander Motin *mode = MODE_HIADAPTIVE;
5425883360bSNate Lawson else
5435883360bSNate Lawson errx(1, "bad option: -%c %s", (char)ch, optarg);
5445883360bSNate Lawson }
5455883360bSNate Lawson
5465883360bSNate Lawson static void
5475c81ba5aSAndriy Voskoboinyk parse_acline_mode(char *arg, int ch)
5485c81ba5aSAndriy Voskoboinyk {
5495c81ba5aSAndriy Voskoboinyk if (strcmp(arg, "sysctl") == 0)
5505c81ba5aSAndriy Voskoboinyk acline_mode_user = ac_sysctl;
5515c81ba5aSAndriy Voskoboinyk else if (strcmp(arg, "devd") == 0)
5525c81ba5aSAndriy Voskoboinyk acline_mode_user = ac_acpi_devd;
5535c81ba5aSAndriy Voskoboinyk #ifdef USE_APM
5545c81ba5aSAndriy Voskoboinyk else if (strcmp(arg, "apm") == 0)
5555c81ba5aSAndriy Voskoboinyk acline_mode_user = ac_apm;
5565c81ba5aSAndriy Voskoboinyk #endif
557*88a198afSBaptiste Daroussin else if (strcmp(arg, "netlink") == 0)
558*88a198afSBaptiste Daroussin acline_mode_user = ac_acpi_netlink;
5595c81ba5aSAndriy Voskoboinyk else
5605c81ba5aSAndriy Voskoboinyk errx(1, "bad option: -%c %s", (char)ch, optarg);
5615c81ba5aSAndriy Voskoboinyk }
5625c81ba5aSAndriy Voskoboinyk
5635c81ba5aSAndriy Voskoboinyk static void
564ab19351cSNate Lawson handle_sigs(int __unused sig)
565ab19351cSNate Lawson {
566a1819ab5SNate Lawson
567ab19351cSNate Lawson exit_requested = 1;
568ab19351cSNate Lawson }
569ab19351cSNate Lawson
570ab19351cSNate Lawson static void
5715883360bSNate Lawson usage(void)
5725883360bSNate Lawson {
5735883360bSNate Lawson
5745883360bSNate Lawson fprintf(stderr,
575d9138286SColin Percival "usage: powerd [-v] [-a mode] [-b mode] [-i %%] [-m freq] [-M freq] [-N] [-n mode] [-p ival] [-r %%] [-s source] [-P pidfile]\n");
5765883360bSNate Lawson exit(1);
5775883360bSNate Lawson }
5785883360bSNate Lawson
5795883360bSNate Lawson int
5805883360bSNate Lawson main(int argc, char * argv[])
5815883360bSNate Lawson {
5822f7a934cSDag-Erling Smørgrav struct timeval timeout;
5832f7a934cSDag-Erling Smørgrav fd_set fdset;
584*88a198afSBaptiste Daroussin int nfds, rfds;
585a9ed1f7fSPawel Jakub Dawidek struct pidfh *pfh = NULL;
58684148fd1SPawel Jakub Dawidek const char *pidfile = NULL;
5878cb16fdbSAlexander Motin int freq, curfreq, initfreq, *freqs, i, j, *mwatts, numfreqs, load;
588cf2280acSRebecca Cran int minfreq = -1, maxfreq = -1;
589773b251cSAlexander Motin int ch, mode, mode_ac, mode_battery, mode_none, idle, to;
590ab19351cSNate Lawson uint64_t mjoules_used;
5915883360bSNate Lawson size_t len;
592d9138286SColin Percival int nonice;
5935883360bSNate Lawson
5945883360bSNate Lawson /* Default mode for all AC states is adaptive. */
595dbd31977SAlexander Motin mode_ac = mode_none = MODE_HIADAPTIVE;
596dbd31977SAlexander Motin mode_battery = MODE_ADAPTIVE;
5975883360bSNate Lawson cpu_running_mark = DEFAULT_ACTIVE_PERCENT;
5985883360bSNate Lawson cpu_idle_mark = DEFAULT_IDLE_PERCENT;
5995883360bSNate Lawson poll_ival = DEFAULT_POLL_INTERVAL;
600ab19351cSNate Lawson mjoules_used = 0;
6015883360bSNate Lawson vflag = 0;
602d9138286SColin Percival nonice = 0;
6035883360bSNate Lawson
604c996f586SNate Lawson /* User must be root to control frequencies. */
605c996f586SNate Lawson if (geteuid() != 0)
606c996f586SNate Lawson errx(1, "must be root to run");
607c996f586SNate Lawson
608d9138286SColin Percival while ((ch = getopt(argc, argv, "a:b:i:m:M:Nn:p:P:r:s:v")) != -1)
6095883360bSNate Lawson switch (ch) {
6105883360bSNate Lawson case 'a':
6115883360bSNate Lawson parse_mode(optarg, &mode_ac, ch);
6125883360bSNate Lawson break;
6135883360bSNate Lawson case 'b':
6145883360bSNate Lawson parse_mode(optarg, &mode_battery, ch);
6155883360bSNate Lawson break;
6165c81ba5aSAndriy Voskoboinyk case 's':
6175c81ba5aSAndriy Voskoboinyk parse_acline_mode(optarg, ch);
6185c81ba5aSAndriy Voskoboinyk break;
6195883360bSNate Lawson case 'i':
6205883360bSNate Lawson cpu_idle_mark = atoi(optarg);
6215883360bSNate Lawson if (cpu_idle_mark < 0 || cpu_idle_mark > 100) {
6225883360bSNate Lawson warnx("%d is not a valid percent",
6235883360bSNate Lawson cpu_idle_mark);
6245883360bSNate Lawson usage();
6255883360bSNate Lawson }
6265883360bSNate Lawson break;
627cf2280acSRebecca Cran case 'm':
628cf2280acSRebecca Cran minfreq = atoi(optarg);
629cf2280acSRebecca Cran if (minfreq < 0) {
630cf2280acSRebecca Cran warnx("%d is not a valid CPU frequency",
631cf2280acSRebecca Cran minfreq);
632cf2280acSRebecca Cran usage();
633cf2280acSRebecca Cran }
634cf2280acSRebecca Cran break;
635cf2280acSRebecca Cran case 'M':
636cf2280acSRebecca Cran maxfreq = atoi(optarg);
637cf2280acSRebecca Cran if (maxfreq < 0) {
638cf2280acSRebecca Cran warnx("%d is not a valid CPU frequency",
639cf2280acSRebecca Cran maxfreq);
640cf2280acSRebecca Cran usage();
641cf2280acSRebecca Cran }
642cf2280acSRebecca Cran break;
643d9138286SColin Percival case 'N':
644d9138286SColin Percival nonice = 1;
645d9138286SColin Percival break;
6465883360bSNate Lawson case 'n':
6475883360bSNate Lawson parse_mode(optarg, &mode_none, ch);
6485883360bSNate Lawson break;
6495883360bSNate Lawson case 'p':
6505883360bSNate Lawson poll_ival = atoi(optarg);
6515883360bSNate Lawson if (poll_ival < 5) {
6525883360bSNate Lawson warnx("poll interval is in units of ms");
6535883360bSNate Lawson usage();
6545883360bSNate Lawson }
6555883360bSNate Lawson break;
65684148fd1SPawel Jakub Dawidek case 'P':
65784148fd1SPawel Jakub Dawidek pidfile = optarg;
65884148fd1SPawel Jakub Dawidek break;
6595883360bSNate Lawson case 'r':
6605883360bSNate Lawson cpu_running_mark = atoi(optarg);
661dbd31977SAlexander Motin if (cpu_running_mark <= 0 || cpu_running_mark > 100) {
6625883360bSNate Lawson warnx("%d is not a valid percent",
6635883360bSNate Lawson cpu_running_mark);
6645883360bSNate Lawson usage();
6655883360bSNate Lawson }
6665883360bSNate Lawson break;
6675883360bSNate Lawson case 'v':
6685883360bSNate Lawson vflag = 1;
6695883360bSNate Lawson break;
6705883360bSNate Lawson default:
6715883360bSNate Lawson usage();
6725883360bSNate Lawson }
6735883360bSNate Lawson
674a1819ab5SNate Lawson mode = mode_none;
675a1819ab5SNate Lawson
6765883360bSNate Lawson /* Poll interval is in units of ms. */
6775883360bSNate Lawson poll_ival *= 1000;
6785883360bSNate Lawson
6795883360bSNate Lawson /* Look up various sysctl MIBs. */
6805883360bSNate Lawson len = 2;
681dbd31977SAlexander Motin if (sysctlnametomib("kern.cp_times", cp_times_mib, &len))
682dbd31977SAlexander Motin err(1, "lookup kern.cp_times");
6835883360bSNate Lawson len = 4;
6845883360bSNate Lawson if (sysctlnametomib("dev.cpu.0.freq", freq_mib, &len))
6857b3b3683SRobert Millan err(EX_UNAVAILABLE, "no cpufreq(4) support -- aborting");
6865883360bSNate Lawson len = 4;
6875883360bSNate Lawson if (sysctlnametomib("dev.cpu.0.freq_levels", levels_mib, &len))
6885883360bSNate Lawson err(1, "lookup freq_levels");
6895883360bSNate Lawson
690dbd31977SAlexander Motin /* Check if we can read the load and supported freqs. */
691d9138286SColin Percival if (read_usage_times(NULL, nonice))
6925883360bSNate Lawson err(1, "read_usage_times");
693cf2280acSRebecca Cran if (read_freqs(&numfreqs, &freqs, &mwatts, minfreq, maxfreq))
6945883360bSNate Lawson err(1, "error reading supported CPU frequencies");
695cf2280acSRebecca Cran if (numfreqs == 0)
696cf2280acSRebecca Cran errx(1, "no CPU frequencies in user-specified range");
6975883360bSNate Lawson
6985883360bSNate Lawson /* Run in the background unless in verbose mode. */
69984148fd1SPawel Jakub Dawidek if (!vflag) {
70084148fd1SPawel Jakub Dawidek pid_t otherpid;
70184148fd1SPawel Jakub Dawidek
70284148fd1SPawel Jakub Dawidek pfh = pidfile_open(pidfile, 0600, &otherpid);
70384148fd1SPawel Jakub Dawidek if (pfh == NULL) {
70484148fd1SPawel Jakub Dawidek if (errno == EEXIST) {
70584148fd1SPawel Jakub Dawidek errx(1, "powerd already running, pid: %d",
70684148fd1SPawel Jakub Dawidek otherpid);
70784148fd1SPawel Jakub Dawidek }
70884148fd1SPawel Jakub Dawidek warn("cannot open pid file");
70984148fd1SPawel Jakub Dawidek }
71072699a22SNate Lawson if (daemon(0, 0) != 0) {
71172699a22SNate Lawson warn("cannot enter daemon mode, exiting");
71272699a22SNate Lawson pidfile_remove(pfh);
71372699a22SNate Lawson exit(EXIT_FAILURE);
71472699a22SNate Lawson
71572699a22SNate Lawson }
71684148fd1SPawel Jakub Dawidek pidfile_write(pfh);
71784148fd1SPawel Jakub Dawidek }
7185883360bSNate Lawson
71933a00fc0SNate Lawson /* Decide whether to use ACPI or APM to read the AC line status. */
72033a00fc0SNate Lawson acline_init();
72133a00fc0SNate Lawson
7222f7a934cSDag-Erling Smørgrav /*
7232f7a934cSDag-Erling Smørgrav * Exit cleanly on signals.
7242f7a934cSDag-Erling Smørgrav */
7252f7a934cSDag-Erling Smørgrav signal(SIGINT, handle_sigs);
7262f7a934cSDag-Erling Smørgrav signal(SIGTERM, handle_sigs);
7272f7a934cSDag-Erling Smørgrav
728773b251cSAlexander Motin freq = initfreq = curfreq = get_freq();
729773b251cSAlexander Motin i = get_freq_id(curfreq, freqs, numfreqs);
730dbd31977SAlexander Motin if (freq < 1)
731dbd31977SAlexander Motin freq = 1;
732cf2280acSRebecca Cran
733cf2280acSRebecca Cran /*
734cf2280acSRebecca Cran * If we are in adaptive mode and the current frequency is outside the
735cf2280acSRebecca Cran * user-defined range, adjust it to be within the user-defined range.
736cf2280acSRebecca Cran */
737*88a198afSBaptiste Daroussin acline_read(0);
738cf2280acSRebecca Cran if (acline_status > SRC_UNKNOWN)
739cf2280acSRebecca Cran errx(1, "invalid AC line status %d", acline_status);
740cf2280acSRebecca Cran if ((acline_status == SRC_AC &&
741cf2280acSRebecca Cran (mode_ac == MODE_ADAPTIVE || mode_ac == MODE_HIADAPTIVE)) ||
742cf2280acSRebecca Cran (acline_status == SRC_BATTERY &&
743cf2280acSRebecca Cran (mode_battery == MODE_ADAPTIVE || mode_battery == MODE_HIADAPTIVE)) ||
744cf2280acSRebecca Cran (acline_status == SRC_UNKNOWN &&
745cf2280acSRebecca Cran (mode_none == MODE_ADAPTIVE || mode_none == MODE_HIADAPTIVE))) {
746cf2280acSRebecca Cran /* Read the current frequency. */
747cf2280acSRebecca Cran len = sizeof(curfreq);
748cf2280acSRebecca Cran if (sysctl(freq_mib, 4, &curfreq, &len, NULL, 0) != 0) {
749cf2280acSRebecca Cran if (vflag)
750cf2280acSRebecca Cran warn("error reading current CPU frequency");
751cf2280acSRebecca Cran }
752cf2280acSRebecca Cran if (curfreq < freqs[numfreqs - 1]) {
753cf2280acSRebecca Cran if (vflag) {
754cf2280acSRebecca Cran printf("CPU frequency is below user-defined "
755cf2280acSRebecca Cran "minimum; changing frequency to %d "
756cf2280acSRebecca Cran "MHz\n", freqs[numfreqs - 1]);
757cf2280acSRebecca Cran }
758cf2280acSRebecca Cran if (set_freq(freqs[numfreqs - 1]) != 0) {
759cf2280acSRebecca Cran warn("error setting CPU freq %d",
760cf2280acSRebecca Cran freqs[numfreqs - 1]);
761cf2280acSRebecca Cran }
762cf2280acSRebecca Cran } else if (curfreq > freqs[0]) {
763cf2280acSRebecca Cran if (vflag) {
764cf2280acSRebecca Cran printf("CPU frequency is above user-defined "
765cf2280acSRebecca Cran "maximum; changing frequency to %d "
766cf2280acSRebecca Cran "MHz\n", freqs[0]);
767cf2280acSRebecca Cran }
768cf2280acSRebecca Cran if (set_freq(freqs[0]) != 0) {
769cf2280acSRebecca Cran warn("error setting CPU freq %d",
770cf2280acSRebecca Cran freqs[0]);
771cf2280acSRebecca Cran }
772cf2280acSRebecca Cran }
773cf2280acSRebecca Cran }
774cf2280acSRebecca Cran
775773b251cSAlexander Motin idle = 0;
7765883360bSNate Lawson /* Main loop. */
7775883360bSNate Lawson for (;;) {
7782f7a934cSDag-Erling Smørgrav FD_ZERO(&fdset);
7792f7a934cSDag-Erling Smørgrav if (devd_pipe >= 0) {
7802f7a934cSDag-Erling Smørgrav FD_SET(devd_pipe, &fdset);
7812f7a934cSDag-Erling Smørgrav nfds = devd_pipe + 1;
782*88a198afSBaptiste Daroussin } else if (acline_mode == ac_acpi_netlink) {
783*88a198afSBaptiste Daroussin FD_SET(ss.fd, &fdset);
784*88a198afSBaptiste Daroussin nfds = ss.fd + 1;
7852f7a934cSDag-Erling Smørgrav } else {
7862f7a934cSDag-Erling Smørgrav nfds = 0;
7872f7a934cSDag-Erling Smørgrav }
788773b251cSAlexander Motin if (mode == MODE_HIADAPTIVE || idle < 120)
789773b251cSAlexander Motin to = poll_ival;
790773b251cSAlexander Motin else if (idle < 360)
791773b251cSAlexander Motin to = poll_ival * 2;
792773b251cSAlexander Motin else
793773b251cSAlexander Motin to = poll_ival * 4;
794773b251cSAlexander Motin timeout.tv_sec = to / 1000000;
795773b251cSAlexander Motin timeout.tv_usec = to % 1000000;
796*88a198afSBaptiste Daroussin rfds = select(nfds, &fdset, NULL, &fdset, &timeout);
7975883360bSNate Lawson
798ab19351cSNate Lawson /* If the user requested we quit, print some statistics. */
799ab19351cSNate Lawson if (exit_requested) {
800ab19351cSNate Lawson if (vflag && mjoules_used != 0)
801ab19351cSNate Lawson printf("total joules used: %u.%03u\n",
802ab19351cSNate Lawson (u_int)(mjoules_used / 1000),
803ab19351cSNate Lawson (int)mjoules_used % 1000);
804ab19351cSNate Lawson break;
805ab19351cSNate Lawson }
806ab19351cSNate Lawson
8075883360bSNate Lawson /* Read the current AC status and record the mode. */
808*88a198afSBaptiste Daroussin acline_read(rfds);
8092f7a934cSDag-Erling Smørgrav switch (acline_status) {
8105883360bSNate Lawson case SRC_AC:
8115883360bSNate Lawson mode = mode_ac;
8125883360bSNate Lawson break;
8135883360bSNate Lawson case SRC_BATTERY:
8145883360bSNate Lawson mode = mode_battery;
8155883360bSNate Lawson break;
8165883360bSNate Lawson case SRC_UNKNOWN:
8175883360bSNate Lawson mode = mode_none;
8185883360bSNate Lawson break;
8195883360bSNate Lawson default:
8202f7a934cSDag-Erling Smørgrav errx(1, "invalid AC line status %d", acline_status);
8215883360bSNate Lawson }
8225883360bSNate Lawson
8235883360bSNate Lawson /* Read the current frequency. */
824773b251cSAlexander Motin if (idle % 32 == 0) {
825dbd31977SAlexander Motin if ((curfreq = get_freq()) == 0)
826a1819ab5SNate Lawson continue;
827dbd31977SAlexander Motin i = get_freq_id(curfreq, freqs, numfreqs);
828773b251cSAlexander Motin }
829773b251cSAlexander Motin idle++;
830ab19351cSNate Lawson if (vflag) {
831ab19351cSNate Lawson /* Keep a sum of all power actually used. */
832dbd31977SAlexander Motin if (mwatts[i] != -1)
833ab19351cSNate Lawson mjoules_used +=
834ab19351cSNate Lawson (mwatts[i] * (poll_ival / 1000)) / 1000;
835ab19351cSNate Lawson }
836ab19351cSNate Lawson
8375883360bSNate Lawson /* Always switch to the lowest frequency in min mode. */
8385883360bSNate Lawson if (mode == MODE_MIN) {
839dbd31977SAlexander Motin freq = freqs[numfreqs - 1];
840dbd31977SAlexander Motin if (curfreq != freq) {
8415883360bSNate Lawson if (vflag) {
8425883360bSNate Lawson printf("now operating on %s power; "
8435883360bSNate Lawson "changing frequency to %d MHz\n",
844dbd31977SAlexander Motin modes[acline_status], freq);
8455883360bSNate Lawson }
846773b251cSAlexander Motin idle = 0;
847dbd31977SAlexander Motin if (set_freq(freq) != 0) {
848a1819ab5SNate Lawson warn("error setting CPU freq %d",
849dbd31977SAlexander Motin freq);
850a1819ab5SNate Lawson continue;
851a1819ab5SNate Lawson }
8525883360bSNate Lawson }
8535883360bSNate Lawson continue;
8545883360bSNate Lawson }
8555883360bSNate Lawson
8565883360bSNate Lawson /* Always switch to the highest frequency in max mode. */
8575883360bSNate Lawson if (mode == MODE_MAX) {
858dbd31977SAlexander Motin freq = freqs[0];
859dbd31977SAlexander Motin if (curfreq != freq) {
8605883360bSNate Lawson if (vflag) {
86148bd7109SNate Lawson printf("now operating on %s power; "
8625883360bSNate Lawson "changing frequency to %d MHz\n",
863dbd31977SAlexander Motin modes[acline_status], freq);
8645883360bSNate Lawson }
865773b251cSAlexander Motin idle = 0;
866dbd31977SAlexander Motin if (set_freq(freq) != 0) {
867a1819ab5SNate Lawson warn("error setting CPU freq %d",
868dbd31977SAlexander Motin freq);
869a1819ab5SNate Lawson continue;
870a1819ab5SNate Lawson }
8715883360bSNate Lawson }
8725883360bSNate Lawson continue;
8735883360bSNate Lawson }
8745883360bSNate Lawson
8755883360bSNate Lawson /* Adaptive mode; get the current CPU usage times. */
876d9138286SColin Percival if (read_usage_times(&load, nonice)) {
877a1819ab5SNate Lawson if (vflag)
878a1819ab5SNate Lawson warn("read_usage_times() failed");
879a1819ab5SNate Lawson continue;
880a1819ab5SNate Lawson }
8815883360bSNate Lawson
882dbd31977SAlexander Motin if (mode == MODE_ADAPTIVE) {
883dbd31977SAlexander Motin if (load > cpu_running_mark) {
884dbd31977SAlexander Motin if (load > 95 || load > cpu_running_mark * 2)
885dbd31977SAlexander Motin freq *= 2;
886dbd31977SAlexander Motin else
887dbd31977SAlexander Motin freq = freq * load / cpu_running_mark;
888dbd31977SAlexander Motin if (freq > freqs[0])
889dbd31977SAlexander Motin freq = freqs[0];
890dbd31977SAlexander Motin } else if (load < cpu_idle_mark &&
891dbd31977SAlexander Motin curfreq * load < freqs[get_freq_id(
892dbd31977SAlexander Motin freq * 7 / 8, freqs, numfreqs)] *
893dbd31977SAlexander Motin cpu_running_mark) {
894dbd31977SAlexander Motin freq = freq * 7 / 8;
895dbd31977SAlexander Motin if (freq < freqs[numfreqs - 1])
896dbd31977SAlexander Motin freq = freqs[numfreqs - 1];
8977065e0e9SBruno Ducrot }
898dbd31977SAlexander Motin } else { /* MODE_HIADAPTIVE */
899dbd31977SAlexander Motin if (load > cpu_running_mark / 2) {
900dbd31977SAlexander Motin if (load > 95 || load > cpu_running_mark)
901dbd31977SAlexander Motin freq *= 4;
902dbd31977SAlexander Motin else
903dbd31977SAlexander Motin freq = freq * load * 2 / cpu_running_mark;
904dbd31977SAlexander Motin if (freq > freqs[0] * 2)
905dbd31977SAlexander Motin freq = freqs[0] * 2;
906dbd31977SAlexander Motin } else if (load < cpu_idle_mark / 2 &&
907dbd31977SAlexander Motin curfreq * load < freqs[get_freq_id(
908dbd31977SAlexander Motin freq * 31 / 32, freqs, numfreqs)] *
909dbd31977SAlexander Motin cpu_running_mark / 2) {
910dbd31977SAlexander Motin freq = freq * 31 / 32;
911dbd31977SAlexander Motin if (freq < freqs[numfreqs - 1])
912dbd31977SAlexander Motin freq = freqs[numfreqs - 1];
913dbd31977SAlexander Motin }
914dbd31977SAlexander Motin }
9155883360bSNate Lawson if (vflag) {
916dbd31977SAlexander Motin printf("load %3d%%, current freq %4d MHz (%2d), wanted freq %4d MHz\n",
917dbd31977SAlexander Motin load, curfreq, i, freq);
9185883360bSNate Lawson }
919dbd31977SAlexander Motin j = get_freq_id(freq, freqs, numfreqs);
920dbd31977SAlexander Motin if (i != j) {
9215883360bSNate Lawson if (vflag) {
922dbd31977SAlexander Motin printf("changing clock"
9235883360bSNate Lawson " speed from %d MHz to %d MHz\n",
924dbd31977SAlexander Motin freqs[i], freqs[j]);
9255883360bSNate Lawson }
926773b251cSAlexander Motin idle = 0;
927dbd31977SAlexander Motin if (set_freq(freqs[j]))
928a1819ab5SNate Lawson warn("error setting CPU frequency %d",
929dbd31977SAlexander Motin freqs[j]);
9305883360bSNate Lawson }
9315883360bSNate Lawson }
9328cb16fdbSAlexander Motin if (set_freq(initfreq))
9338cb16fdbSAlexander Motin warn("error setting CPU frequency %d", initfreq);
934ab19351cSNate Lawson free(freqs);
935ab19351cSNate Lawson free(mwatts);
936a1819ab5SNate Lawson devd_close();
93784148fd1SPawel Jakub Dawidek if (!vflag)
93884148fd1SPawel Jakub Dawidek pidfile_remove(pfh);
9395883360bSNate Lawson
9405883360bSNate Lawson exit(0);
9415883360bSNate Lawson }
942