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> 3288a198afSBaptiste Daroussin #include <sys/linker.h> 3388a198afSBaptiste 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 4088a198afSBaptiste Daroussin #include <netlink/netlink.h> 4188a198afSBaptiste Daroussin #include <netlink/netlink_generic.h> 4288a198afSBaptiste Daroussin #include <netlink/netlink_snl.h> 4388a198afSBaptiste Daroussin #include <netlink/netlink_snl_generic.h> 4488a198afSBaptiste Daroussin #include <netlink/netlink_sysevent.h> 4588a198afSBaptiste 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> 5188a198afSBaptiste 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); 10088a198afSBaptiste Daroussin static void acline_read(int rfds); 10188a198afSBaptiste 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 13088a198afSBaptiste 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; 13888a198afSBaptiste Daroussin static bool try_netlink = true; 13988a198afSBaptiste 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 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 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 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 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 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 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 34188a198afSBaptiste Daroussin struct nlevent { 34288a198afSBaptiste Daroussin const char *name; 34388a198afSBaptiste Daroussin const char *subsystem; 34488a198afSBaptiste Daroussin const char *type; 34588a198afSBaptiste Daroussin const char *data; 34688a198afSBaptiste Daroussin }; 34788a198afSBaptiste Daroussin #define _OUT(_field) offsetof(struct nlevent, _field) 34888a198afSBaptiste Daroussin static struct snl_attr_parser ap_nlevent_get[] = { 34988a198afSBaptiste Daroussin { .type = NLSE_ATTR_SYSTEM, .off = _OUT(name), .cb = snl_attr_get_string }, 35088a198afSBaptiste Daroussin { .type = NLSE_ATTR_SUBSYSTEM, .off = _OUT(subsystem), .cb = snl_attr_get_string }, 35188a198afSBaptiste Daroussin { .type = NLSE_ATTR_TYPE, .off = _OUT(type), .cb = snl_attr_get_string }, 35288a198afSBaptiste Daroussin { .type = NLSE_ATTR_DATA, .off = _OUT(data), .cb = snl_attr_get_string }, 35388a198afSBaptiste Daroussin }; 35488a198afSBaptiste Daroussin #undef _OUT 35588a198afSBaptiste Daroussin 35688a198afSBaptiste Daroussin SNL_DECLARE_GENL_PARSER(nlevent_get_parser, ap_nlevent_get); 35788a198afSBaptiste Daroussin 3582f7a934cSDag-Erling Smørgrav static void 35988a198afSBaptiste Daroussin acline_read(int rfds) 36048bd7109SNate Lawson { 36188a198afSBaptiste Daroussin if (acline_mode == ac_acpi_netlink) { 36288a198afSBaptiste Daroussin struct nlmsghdr *hdr; 36388a198afSBaptiste Daroussin struct nlevent ne; 36488a198afSBaptiste Daroussin char *ptr; 36588a198afSBaptiste Daroussin int notify; 36688a198afSBaptiste Daroussin 36788a198afSBaptiste Daroussin if (rfds == 0) 36888a198afSBaptiste Daroussin return; 36988a198afSBaptiste Daroussin hdr = snl_read_message(&ss); 37088a198afSBaptiste Daroussin if (hdr != NULL && hdr->nlmsg_type != NLMSG_ERROR) { 37188a198afSBaptiste Daroussin memset(&ne, 0, sizeof(ne)); 37288a198afSBaptiste Daroussin if (!snl_parse_nlmsg(&ss, hdr, &nlevent_get_parser, &ne)) 37388a198afSBaptiste Daroussin return; 37488a198afSBaptiste Daroussin if (strcmp(ne.subsystem, "ACAD") != 0) 37588a198afSBaptiste Daroussin return; 37688a198afSBaptiste Daroussin if ((ptr = strstr(ne.data, "notify=")) != NULL && 37788a198afSBaptiste Daroussin sscanf(ptr, "notify=%x", ¬ify) == 1) 37888a198afSBaptiste Daroussin acline_status = (notify ? SRC_AC : SRC_BATTERY); 37988a198afSBaptiste Daroussin } 38088a198afSBaptiste Daroussin return; 38188a198afSBaptiste Daroussin 38288a198afSBaptiste 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 || 43888a198afSBaptiste Daroussin acline_mode_user == ac_acpi_devd || 43988a198afSBaptiste Daroussin acline_mode_user == ac_acpi_netlink)) { 4405c81ba5aSAndriy Voskoboinyk #endif 4412f7a934cSDag-Erling Smørgrav struct timeval now; 44248bd7109SNate Lawson 44388a198afSBaptiste Daroussin if (acline_mode_user != ac_acpi_devd && try_netlink) { 44488a198afSBaptiste Daroussin try_netlink = false; /* only try once */ 44588a198afSBaptiste Daroussin if (netlink_init()) { 44688a198afSBaptiste Daroussin if (vflag) 44788a198afSBaptiste Daroussin warnx("using netlink for AC line status"); 44888a198afSBaptiste Daroussin acline_mode = ac_acpi_netlink; 44988a198afSBaptiste Daroussin } 45088a198afSBaptiste Daroussin return; 45188a198afSBaptiste 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 46488a198afSBaptiste Daroussin bool 46588a198afSBaptiste Daroussin netlink_init(void) 46688a198afSBaptiste Daroussin { 467*c1f6b799SGleb Smirnoff uint32_t group; 46888a198afSBaptiste Daroussin 46988a198afSBaptiste Daroussin if (modfind("nlsysevent") < 0) 47088a198afSBaptiste Daroussin kldload("nlsysevent"); 47188a198afSBaptiste Daroussin if (modfind("nlsysevent") < 0) 47288a198afSBaptiste Daroussin return (false); 47388a198afSBaptiste Daroussin 474*c1f6b799SGleb Smirnoff if (!snl_init(&ss, NETLINK_GENERIC) || (group = 475*c1f6b799SGleb Smirnoff snl_get_genl_mcast_group(&ss, "nlsysevent", "ACPI", NULL)) == 0) { 476*c1f6b799SGleb Smirnoff warnx("Cannot find \"nlsysevent\" family \"ACPI\" group"); 47788a198afSBaptiste Daroussin return (false); 478*c1f6b799SGleb Smirnoff } 47988a198afSBaptiste Daroussin 480*c1f6b799SGleb Smirnoff if (setsockopt(ss.fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group, 481*c1f6b799SGleb Smirnoff sizeof(group)) == -1) { 48288a198afSBaptiste Daroussin warnx("Cannot subscribe to \"ACPI\""); 48388a198afSBaptiste Daroussin return (false); 48488a198afSBaptiste Daroussin } 48588a198afSBaptiste Daroussin return (true); 48688a198afSBaptiste Daroussin } 48788a198afSBaptiste Daroussin 488a1819ab5SNate Lawson static int 489a1819ab5SNate Lawson devd_init(void) 490a1819ab5SNate Lawson { 491a1819ab5SNate Lawson struct sockaddr_un devd_addr; 492a1819ab5SNate Lawson 493a1819ab5SNate Lawson bzero(&devd_addr, sizeof(devd_addr)); 49479477f3cSBaptiste Daroussin if ((devd_pipe = socket(PF_LOCAL, SOCK_STREAM|SOCK_NONBLOCK, 0)) < 0) { 495a1819ab5SNate Lawson if (vflag) 4962f7a934cSDag-Erling Smørgrav warn("%s(): socket()", __func__); 497a1819ab5SNate Lawson return (-1); 498a1819ab5SNate Lawson } 499a1819ab5SNate Lawson 500a1819ab5SNate Lawson devd_addr.sun_family = PF_LOCAL; 501a1819ab5SNate Lawson strlcpy(devd_addr.sun_path, DEVDPIPE, sizeof(devd_addr.sun_path)); 5022f7a934cSDag-Erling Smørgrav if (connect(devd_pipe, (struct sockaddr *)&devd_addr, 503a1819ab5SNate Lawson sizeof(devd_addr)) == -1) { 5042f7a934cSDag-Erling Smørgrav if (vflag) 5052f7a934cSDag-Erling Smørgrav warn("%s(): connect()", __func__); 5062f7a934cSDag-Erling Smørgrav close(devd_pipe); 5072f7a934cSDag-Erling Smørgrav devd_pipe = -1; 508a1819ab5SNate Lawson return (-1); 509a1819ab5SNate Lawson } 510a1819ab5SNate Lawson 5112f7a934cSDag-Erling Smørgrav return (devd_pipe); 512a1819ab5SNate Lawson } 513a1819ab5SNate Lawson 514a1819ab5SNate Lawson static void 515a1819ab5SNate Lawson devd_close(void) 516a1819ab5SNate Lawson { 517a1819ab5SNate Lawson 518a1819ab5SNate Lawson close(devd_pipe); 519a1819ab5SNate Lawson devd_pipe = -1; 52048bd7109SNate Lawson } 52148bd7109SNate Lawson 5225883360bSNate Lawson static void 5235883360bSNate Lawson parse_mode(char *arg, int *mode, int ch) 5245883360bSNate Lawson { 5255883360bSNate Lawson 526db20dc4dSDag-Erling Smørgrav if (strcmp(arg, "minimum") == 0 || strcmp(arg, "min") == 0) 5275883360bSNate Lawson *mode = MODE_MIN; 528db20dc4dSDag-Erling Smørgrav else if (strcmp(arg, "maximum") == 0 || strcmp(arg, "max") == 0) 5295883360bSNate Lawson *mode = MODE_MAX; 530dc70a966SDavid E. O'Brien else if (strcmp(arg, "adaptive") == 0 || strcmp(arg, "adp") == 0) 5315883360bSNate Lawson *mode = MODE_ADAPTIVE; 532dbd31977SAlexander Motin else if (strcmp(arg, "hiadaptive") == 0 || strcmp(arg, "hadp") == 0) 533dbd31977SAlexander Motin *mode = MODE_HIADAPTIVE; 5345883360bSNate Lawson else 5355883360bSNate Lawson errx(1, "bad option: -%c %s", (char)ch, optarg); 5365883360bSNate Lawson } 5375883360bSNate Lawson 5385883360bSNate Lawson static void 5395c81ba5aSAndriy Voskoboinyk parse_acline_mode(char *arg, int ch) 5405c81ba5aSAndriy Voskoboinyk { 5415c81ba5aSAndriy Voskoboinyk if (strcmp(arg, "sysctl") == 0) 5425c81ba5aSAndriy Voskoboinyk acline_mode_user = ac_sysctl; 5435c81ba5aSAndriy Voskoboinyk else if (strcmp(arg, "devd") == 0) 5445c81ba5aSAndriy Voskoboinyk acline_mode_user = ac_acpi_devd; 5455c81ba5aSAndriy Voskoboinyk #ifdef USE_APM 5465c81ba5aSAndriy Voskoboinyk else if (strcmp(arg, "apm") == 0) 5475c81ba5aSAndriy Voskoboinyk acline_mode_user = ac_apm; 5485c81ba5aSAndriy Voskoboinyk #endif 54988a198afSBaptiste Daroussin else if (strcmp(arg, "netlink") == 0) 55088a198afSBaptiste Daroussin acline_mode_user = ac_acpi_netlink; 5515c81ba5aSAndriy Voskoboinyk else 5525c81ba5aSAndriy Voskoboinyk errx(1, "bad option: -%c %s", (char)ch, optarg); 5535c81ba5aSAndriy Voskoboinyk } 5545c81ba5aSAndriy Voskoboinyk 5555c81ba5aSAndriy Voskoboinyk static void 556ab19351cSNate Lawson handle_sigs(int __unused sig) 557ab19351cSNate Lawson { 558a1819ab5SNate Lawson 559ab19351cSNate Lawson exit_requested = 1; 560ab19351cSNate Lawson } 561ab19351cSNate Lawson 562ab19351cSNate Lawson static void 5635883360bSNate Lawson usage(void) 5645883360bSNate Lawson { 5655883360bSNate Lawson 5665883360bSNate Lawson fprintf(stderr, 567d9138286SColin Percival "usage: powerd [-v] [-a mode] [-b mode] [-i %%] [-m freq] [-M freq] [-N] [-n mode] [-p ival] [-r %%] [-s source] [-P pidfile]\n"); 5685883360bSNate Lawson exit(1); 5695883360bSNate Lawson } 5705883360bSNate Lawson 5715883360bSNate Lawson int 5725883360bSNate Lawson main(int argc, char * argv[]) 5735883360bSNate Lawson { 5742f7a934cSDag-Erling Smørgrav struct timeval timeout; 5752f7a934cSDag-Erling Smørgrav fd_set fdset; 57688a198afSBaptiste Daroussin int nfds, rfds; 577a9ed1f7fSPawel Jakub Dawidek struct pidfh *pfh = NULL; 57884148fd1SPawel Jakub Dawidek const char *pidfile = NULL; 5798cb16fdbSAlexander Motin int freq, curfreq, initfreq, *freqs, i, j, *mwatts, numfreqs, load; 580cf2280acSRebecca Cran int minfreq = -1, maxfreq = -1; 581773b251cSAlexander Motin int ch, mode, mode_ac, mode_battery, mode_none, idle, to; 582ab19351cSNate Lawson uint64_t mjoules_used; 5835883360bSNate Lawson size_t len; 584d9138286SColin Percival int nonice; 5855883360bSNate Lawson 5865883360bSNate Lawson /* Default mode for all AC states is adaptive. */ 587dbd31977SAlexander Motin mode_ac = mode_none = MODE_HIADAPTIVE; 588dbd31977SAlexander Motin mode_battery = MODE_ADAPTIVE; 5895883360bSNate Lawson cpu_running_mark = DEFAULT_ACTIVE_PERCENT; 5905883360bSNate Lawson cpu_idle_mark = DEFAULT_IDLE_PERCENT; 5915883360bSNate Lawson poll_ival = DEFAULT_POLL_INTERVAL; 592ab19351cSNate Lawson mjoules_used = 0; 5935883360bSNate Lawson vflag = 0; 594d9138286SColin Percival nonice = 0; 5955883360bSNate Lawson 596c996f586SNate Lawson /* User must be root to control frequencies. */ 597c996f586SNate Lawson if (geteuid() != 0) 598c996f586SNate Lawson errx(1, "must be root to run"); 599c996f586SNate Lawson 600d9138286SColin Percival while ((ch = getopt(argc, argv, "a:b:i:m:M:Nn:p:P:r:s:v")) != -1) 6015883360bSNate Lawson switch (ch) { 6025883360bSNate Lawson case 'a': 6035883360bSNate Lawson parse_mode(optarg, &mode_ac, ch); 6045883360bSNate Lawson break; 6055883360bSNate Lawson case 'b': 6065883360bSNate Lawson parse_mode(optarg, &mode_battery, ch); 6075883360bSNate Lawson break; 6085c81ba5aSAndriy Voskoboinyk case 's': 6095c81ba5aSAndriy Voskoboinyk parse_acline_mode(optarg, ch); 6105c81ba5aSAndriy Voskoboinyk break; 6115883360bSNate Lawson case 'i': 6125883360bSNate Lawson cpu_idle_mark = atoi(optarg); 6135883360bSNate Lawson if (cpu_idle_mark < 0 || cpu_idle_mark > 100) { 6145883360bSNate Lawson warnx("%d is not a valid percent", 6155883360bSNate Lawson cpu_idle_mark); 6165883360bSNate Lawson usage(); 6175883360bSNate Lawson } 6185883360bSNate Lawson break; 619cf2280acSRebecca Cran case 'm': 620cf2280acSRebecca Cran minfreq = atoi(optarg); 621cf2280acSRebecca Cran if (minfreq < 0) { 622cf2280acSRebecca Cran warnx("%d is not a valid CPU frequency", 623cf2280acSRebecca Cran minfreq); 624cf2280acSRebecca Cran usage(); 625cf2280acSRebecca Cran } 626cf2280acSRebecca Cran break; 627cf2280acSRebecca Cran case 'M': 628cf2280acSRebecca Cran maxfreq = atoi(optarg); 629cf2280acSRebecca Cran if (maxfreq < 0) { 630cf2280acSRebecca Cran warnx("%d is not a valid CPU frequency", 631cf2280acSRebecca Cran maxfreq); 632cf2280acSRebecca Cran usage(); 633cf2280acSRebecca Cran } 634cf2280acSRebecca Cran break; 635d9138286SColin Percival case 'N': 636d9138286SColin Percival nonice = 1; 637d9138286SColin Percival break; 6385883360bSNate Lawson case 'n': 6395883360bSNate Lawson parse_mode(optarg, &mode_none, ch); 6405883360bSNate Lawson break; 6415883360bSNate Lawson case 'p': 6425883360bSNate Lawson poll_ival = atoi(optarg); 6435883360bSNate Lawson if (poll_ival < 5) { 6445883360bSNate Lawson warnx("poll interval is in units of ms"); 6455883360bSNate Lawson usage(); 6465883360bSNate Lawson } 6475883360bSNate Lawson break; 64884148fd1SPawel Jakub Dawidek case 'P': 64984148fd1SPawel Jakub Dawidek pidfile = optarg; 65084148fd1SPawel Jakub Dawidek break; 6515883360bSNate Lawson case 'r': 6525883360bSNate Lawson cpu_running_mark = atoi(optarg); 653dbd31977SAlexander Motin if (cpu_running_mark <= 0 || cpu_running_mark > 100) { 6545883360bSNate Lawson warnx("%d is not a valid percent", 6555883360bSNate Lawson cpu_running_mark); 6565883360bSNate Lawson usage(); 6575883360bSNate Lawson } 6585883360bSNate Lawson break; 6595883360bSNate Lawson case 'v': 6605883360bSNate Lawson vflag = 1; 6615883360bSNate Lawson break; 6625883360bSNate Lawson default: 6635883360bSNate Lawson usage(); 6645883360bSNate Lawson } 6655883360bSNate Lawson 666a1819ab5SNate Lawson mode = mode_none; 667a1819ab5SNate Lawson 6685883360bSNate Lawson /* Poll interval is in units of ms. */ 6695883360bSNate Lawson poll_ival *= 1000; 6705883360bSNate Lawson 6715883360bSNate Lawson /* Look up various sysctl MIBs. */ 6725883360bSNate Lawson len = 2; 673dbd31977SAlexander Motin if (sysctlnametomib("kern.cp_times", cp_times_mib, &len)) 674dbd31977SAlexander Motin err(1, "lookup kern.cp_times"); 6755883360bSNate Lawson len = 4; 6765883360bSNate Lawson if (sysctlnametomib("dev.cpu.0.freq", freq_mib, &len)) 6777b3b3683SRobert Millan err(EX_UNAVAILABLE, "no cpufreq(4) support -- aborting"); 6785883360bSNate Lawson len = 4; 6795883360bSNate Lawson if (sysctlnametomib("dev.cpu.0.freq_levels", levels_mib, &len)) 6805883360bSNate Lawson err(1, "lookup freq_levels"); 6815883360bSNate Lawson 682dbd31977SAlexander Motin /* Check if we can read the load and supported freqs. */ 683d9138286SColin Percival if (read_usage_times(NULL, nonice)) 6845883360bSNate Lawson err(1, "read_usage_times"); 685cf2280acSRebecca Cran if (read_freqs(&numfreqs, &freqs, &mwatts, minfreq, maxfreq)) 6865883360bSNate Lawson err(1, "error reading supported CPU frequencies"); 687cf2280acSRebecca Cran if (numfreqs == 0) 688cf2280acSRebecca Cran errx(1, "no CPU frequencies in user-specified range"); 6895883360bSNate Lawson 6905883360bSNate Lawson /* Run in the background unless in verbose mode. */ 69184148fd1SPawel Jakub Dawidek if (!vflag) { 69284148fd1SPawel Jakub Dawidek pid_t otherpid; 69384148fd1SPawel Jakub Dawidek 69484148fd1SPawel Jakub Dawidek pfh = pidfile_open(pidfile, 0600, &otherpid); 69584148fd1SPawel Jakub Dawidek if (pfh == NULL) { 69684148fd1SPawel Jakub Dawidek if (errno == EEXIST) { 69784148fd1SPawel Jakub Dawidek errx(1, "powerd already running, pid: %d", 69884148fd1SPawel Jakub Dawidek otherpid); 69984148fd1SPawel Jakub Dawidek } 70084148fd1SPawel Jakub Dawidek warn("cannot open pid file"); 70184148fd1SPawel Jakub Dawidek } 70272699a22SNate Lawson if (daemon(0, 0) != 0) { 70372699a22SNate Lawson warn("cannot enter daemon mode, exiting"); 70472699a22SNate Lawson pidfile_remove(pfh); 70572699a22SNate Lawson exit(EXIT_FAILURE); 70672699a22SNate Lawson 70772699a22SNate Lawson } 70884148fd1SPawel Jakub Dawidek pidfile_write(pfh); 70984148fd1SPawel Jakub Dawidek } 7105883360bSNate Lawson 71133a00fc0SNate Lawson /* Decide whether to use ACPI or APM to read the AC line status. */ 71233a00fc0SNate Lawson acline_init(); 71333a00fc0SNate Lawson 7142f7a934cSDag-Erling Smørgrav /* 7152f7a934cSDag-Erling Smørgrav * Exit cleanly on signals. 7162f7a934cSDag-Erling Smørgrav */ 7172f7a934cSDag-Erling Smørgrav signal(SIGINT, handle_sigs); 7182f7a934cSDag-Erling Smørgrav signal(SIGTERM, handle_sigs); 7192f7a934cSDag-Erling Smørgrav 720773b251cSAlexander Motin freq = initfreq = curfreq = get_freq(); 721773b251cSAlexander Motin i = get_freq_id(curfreq, freqs, numfreqs); 722dbd31977SAlexander Motin if (freq < 1) 723dbd31977SAlexander Motin freq = 1; 724cf2280acSRebecca Cran 725cf2280acSRebecca Cran /* 726cf2280acSRebecca Cran * If we are in adaptive mode and the current frequency is outside the 727cf2280acSRebecca Cran * user-defined range, adjust it to be within the user-defined range. 728cf2280acSRebecca Cran */ 72988a198afSBaptiste Daroussin acline_read(0); 730cf2280acSRebecca Cran if (acline_status > SRC_UNKNOWN) 731cf2280acSRebecca Cran errx(1, "invalid AC line status %d", acline_status); 732cf2280acSRebecca Cran if ((acline_status == SRC_AC && 733cf2280acSRebecca Cran (mode_ac == MODE_ADAPTIVE || mode_ac == MODE_HIADAPTIVE)) || 734cf2280acSRebecca Cran (acline_status == SRC_BATTERY && 735cf2280acSRebecca Cran (mode_battery == MODE_ADAPTIVE || mode_battery == MODE_HIADAPTIVE)) || 736cf2280acSRebecca Cran (acline_status == SRC_UNKNOWN && 737cf2280acSRebecca Cran (mode_none == MODE_ADAPTIVE || mode_none == MODE_HIADAPTIVE))) { 738cf2280acSRebecca Cran /* Read the current frequency. */ 739cf2280acSRebecca Cran len = sizeof(curfreq); 740cf2280acSRebecca Cran if (sysctl(freq_mib, 4, &curfreq, &len, NULL, 0) != 0) { 741cf2280acSRebecca Cran if (vflag) 742cf2280acSRebecca Cran warn("error reading current CPU frequency"); 743cf2280acSRebecca Cran } 744cf2280acSRebecca Cran if (curfreq < freqs[numfreqs - 1]) { 745cf2280acSRebecca Cran if (vflag) { 746cf2280acSRebecca Cran printf("CPU frequency is below user-defined " 747cf2280acSRebecca Cran "minimum; changing frequency to %d " 748cf2280acSRebecca Cran "MHz\n", freqs[numfreqs - 1]); 749cf2280acSRebecca Cran } 750cf2280acSRebecca Cran if (set_freq(freqs[numfreqs - 1]) != 0) { 751cf2280acSRebecca Cran warn("error setting CPU freq %d", 752cf2280acSRebecca Cran freqs[numfreqs - 1]); 753cf2280acSRebecca Cran } 754cf2280acSRebecca Cran } else if (curfreq > freqs[0]) { 755cf2280acSRebecca Cran if (vflag) { 756cf2280acSRebecca Cran printf("CPU frequency is above user-defined " 757cf2280acSRebecca Cran "maximum; changing frequency to %d " 758cf2280acSRebecca Cran "MHz\n", freqs[0]); 759cf2280acSRebecca Cran } 760cf2280acSRebecca Cran if (set_freq(freqs[0]) != 0) { 761cf2280acSRebecca Cran warn("error setting CPU freq %d", 762cf2280acSRebecca Cran freqs[0]); 763cf2280acSRebecca Cran } 764cf2280acSRebecca Cran } 765cf2280acSRebecca Cran } 766cf2280acSRebecca Cran 767773b251cSAlexander Motin idle = 0; 7685883360bSNate Lawson /* Main loop. */ 7695883360bSNate Lawson for (;;) { 7702f7a934cSDag-Erling Smørgrav FD_ZERO(&fdset); 7712f7a934cSDag-Erling Smørgrav if (devd_pipe >= 0) { 7722f7a934cSDag-Erling Smørgrav FD_SET(devd_pipe, &fdset); 7732f7a934cSDag-Erling Smørgrav nfds = devd_pipe + 1; 77488a198afSBaptiste Daroussin } else if (acline_mode == ac_acpi_netlink) { 77588a198afSBaptiste Daroussin FD_SET(ss.fd, &fdset); 77688a198afSBaptiste Daroussin nfds = ss.fd + 1; 7772f7a934cSDag-Erling Smørgrav } else { 7782f7a934cSDag-Erling Smørgrav nfds = 0; 7792f7a934cSDag-Erling Smørgrav } 780773b251cSAlexander Motin if (mode == MODE_HIADAPTIVE || idle < 120) 781773b251cSAlexander Motin to = poll_ival; 782773b251cSAlexander Motin else if (idle < 360) 783773b251cSAlexander Motin to = poll_ival * 2; 784773b251cSAlexander Motin else 785773b251cSAlexander Motin to = poll_ival * 4; 786773b251cSAlexander Motin timeout.tv_sec = to / 1000000; 787773b251cSAlexander Motin timeout.tv_usec = to % 1000000; 78888a198afSBaptiste Daroussin rfds = select(nfds, &fdset, NULL, &fdset, &timeout); 7895883360bSNate Lawson 790ab19351cSNate Lawson /* If the user requested we quit, print some statistics. */ 791ab19351cSNate Lawson if (exit_requested) { 792ab19351cSNate Lawson if (vflag && mjoules_used != 0) 793ab19351cSNate Lawson printf("total joules used: %u.%03u\n", 794ab19351cSNate Lawson (u_int)(mjoules_used / 1000), 795ab19351cSNate Lawson (int)mjoules_used % 1000); 796ab19351cSNate Lawson break; 797ab19351cSNate Lawson } 798ab19351cSNate Lawson 7995883360bSNate Lawson /* Read the current AC status and record the mode. */ 80088a198afSBaptiste Daroussin acline_read(rfds); 8012f7a934cSDag-Erling Smørgrav switch (acline_status) { 8025883360bSNate Lawson case SRC_AC: 8035883360bSNate Lawson mode = mode_ac; 8045883360bSNate Lawson break; 8055883360bSNate Lawson case SRC_BATTERY: 8065883360bSNate Lawson mode = mode_battery; 8075883360bSNate Lawson break; 8085883360bSNate Lawson case SRC_UNKNOWN: 8095883360bSNate Lawson mode = mode_none; 8105883360bSNate Lawson break; 8115883360bSNate Lawson default: 8122f7a934cSDag-Erling Smørgrav errx(1, "invalid AC line status %d", acline_status); 8135883360bSNate Lawson } 8145883360bSNate Lawson 8155883360bSNate Lawson /* Read the current frequency. */ 816773b251cSAlexander Motin if (idle % 32 == 0) { 817dbd31977SAlexander Motin if ((curfreq = get_freq()) == 0) 818a1819ab5SNate Lawson continue; 819dbd31977SAlexander Motin i = get_freq_id(curfreq, freqs, numfreqs); 820773b251cSAlexander Motin } 821773b251cSAlexander Motin idle++; 822ab19351cSNate Lawson if (vflag) { 823ab19351cSNate Lawson /* Keep a sum of all power actually used. */ 824dbd31977SAlexander Motin if (mwatts[i] != -1) 825ab19351cSNate Lawson mjoules_used += 826ab19351cSNate Lawson (mwatts[i] * (poll_ival / 1000)) / 1000; 827ab19351cSNate Lawson } 828ab19351cSNate Lawson 8295883360bSNate Lawson /* Always switch to the lowest frequency in min mode. */ 8305883360bSNate Lawson if (mode == MODE_MIN) { 831dbd31977SAlexander Motin freq = freqs[numfreqs - 1]; 832dbd31977SAlexander Motin if (curfreq != freq) { 8335883360bSNate Lawson if (vflag) { 8345883360bSNate Lawson printf("now operating on %s power; " 8355883360bSNate Lawson "changing frequency to %d MHz\n", 836dbd31977SAlexander Motin modes[acline_status], freq); 8375883360bSNate Lawson } 838773b251cSAlexander Motin idle = 0; 839dbd31977SAlexander Motin if (set_freq(freq) != 0) { 840a1819ab5SNate Lawson warn("error setting CPU freq %d", 841dbd31977SAlexander Motin freq); 842a1819ab5SNate Lawson continue; 843a1819ab5SNate Lawson } 8445883360bSNate Lawson } 8455883360bSNate Lawson continue; 8465883360bSNate Lawson } 8475883360bSNate Lawson 8485883360bSNate Lawson /* Always switch to the highest frequency in max mode. */ 8495883360bSNate Lawson if (mode == MODE_MAX) { 850dbd31977SAlexander Motin freq = freqs[0]; 851dbd31977SAlexander Motin if (curfreq != freq) { 8525883360bSNate Lawson if (vflag) { 85348bd7109SNate Lawson printf("now operating on %s power; " 8545883360bSNate Lawson "changing frequency to %d MHz\n", 855dbd31977SAlexander Motin modes[acline_status], freq); 8565883360bSNate Lawson } 857773b251cSAlexander Motin idle = 0; 858dbd31977SAlexander Motin if (set_freq(freq) != 0) { 859a1819ab5SNate Lawson warn("error setting CPU freq %d", 860dbd31977SAlexander Motin freq); 861a1819ab5SNate Lawson continue; 862a1819ab5SNate Lawson } 8635883360bSNate Lawson } 8645883360bSNate Lawson continue; 8655883360bSNate Lawson } 8665883360bSNate Lawson 8675883360bSNate Lawson /* Adaptive mode; get the current CPU usage times. */ 868d9138286SColin Percival if (read_usage_times(&load, nonice)) { 869a1819ab5SNate Lawson if (vflag) 870a1819ab5SNate Lawson warn("read_usage_times() failed"); 871a1819ab5SNate Lawson continue; 872a1819ab5SNate Lawson } 8735883360bSNate Lawson 874dbd31977SAlexander Motin if (mode == MODE_ADAPTIVE) { 875dbd31977SAlexander Motin if (load > cpu_running_mark) { 876dbd31977SAlexander Motin if (load > 95 || load > cpu_running_mark * 2) 877dbd31977SAlexander Motin freq *= 2; 878dbd31977SAlexander Motin else 879dbd31977SAlexander Motin freq = freq * load / cpu_running_mark; 880dbd31977SAlexander Motin if (freq > freqs[0]) 881dbd31977SAlexander Motin freq = freqs[0]; 882dbd31977SAlexander Motin } else if (load < cpu_idle_mark && 883dbd31977SAlexander Motin curfreq * load < freqs[get_freq_id( 884dbd31977SAlexander Motin freq * 7 / 8, freqs, numfreqs)] * 885dbd31977SAlexander Motin cpu_running_mark) { 886dbd31977SAlexander Motin freq = freq * 7 / 8; 887dbd31977SAlexander Motin if (freq < freqs[numfreqs - 1]) 888dbd31977SAlexander Motin freq = freqs[numfreqs - 1]; 8897065e0e9SBruno Ducrot } 890dbd31977SAlexander Motin } else { /* MODE_HIADAPTIVE */ 891dbd31977SAlexander Motin if (load > cpu_running_mark / 2) { 892dbd31977SAlexander Motin if (load > 95 || load > cpu_running_mark) 893dbd31977SAlexander Motin freq *= 4; 894dbd31977SAlexander Motin else 895dbd31977SAlexander Motin freq = freq * load * 2 / cpu_running_mark; 896dbd31977SAlexander Motin if (freq > freqs[0] * 2) 897dbd31977SAlexander Motin freq = freqs[0] * 2; 898dbd31977SAlexander Motin } else if (load < cpu_idle_mark / 2 && 899dbd31977SAlexander Motin curfreq * load < freqs[get_freq_id( 900dbd31977SAlexander Motin freq * 31 / 32, freqs, numfreqs)] * 901dbd31977SAlexander Motin cpu_running_mark / 2) { 902dbd31977SAlexander Motin freq = freq * 31 / 32; 903dbd31977SAlexander Motin if (freq < freqs[numfreqs - 1]) 904dbd31977SAlexander Motin freq = freqs[numfreqs - 1]; 905dbd31977SAlexander Motin } 906dbd31977SAlexander Motin } 9075883360bSNate Lawson if (vflag) { 908dbd31977SAlexander Motin printf("load %3d%%, current freq %4d MHz (%2d), wanted freq %4d MHz\n", 909dbd31977SAlexander Motin load, curfreq, i, freq); 9105883360bSNate Lawson } 911dbd31977SAlexander Motin j = get_freq_id(freq, freqs, numfreqs); 912dbd31977SAlexander Motin if (i != j) { 9135883360bSNate Lawson if (vflag) { 914dbd31977SAlexander Motin printf("changing clock" 9155883360bSNate Lawson " speed from %d MHz to %d MHz\n", 916dbd31977SAlexander Motin freqs[i], freqs[j]); 9175883360bSNate Lawson } 918773b251cSAlexander Motin idle = 0; 919dbd31977SAlexander Motin if (set_freq(freqs[j])) 920a1819ab5SNate Lawson warn("error setting CPU frequency %d", 921dbd31977SAlexander Motin freqs[j]); 9225883360bSNate Lawson } 9235883360bSNate Lawson } 9248cb16fdbSAlexander Motin if (set_freq(initfreq)) 9258cb16fdbSAlexander Motin warn("error setting CPU frequency %d", initfreq); 926ab19351cSNate Lawson free(freqs); 927ab19351cSNate Lawson free(mwatts); 928a1819ab5SNate Lawson devd_close(); 92984148fd1SPawel Jakub Dawidek if (!vflag) 93084148fd1SPawel Jakub Dawidek pidfile_remove(pfh); 9315883360bSNate Lawson 9325883360bSNate Lawson exit(0); 9335883360bSNate Lawson } 934