1142577Snjl/*- 2142577Snjl * Copyright (c) 2004 Colin Percival 3142577Snjl * Copyright (c) 2005 Nate Lawson 4142577Snjl * All rights reserved. 5142577Snjl * 6142577Snjl * Redistribution and use in source and binary forms, with or without 7142577Snjl * modification, are permitted providing that the following conditions 8142577Snjl * are met: 9142577Snjl * 1. Redistributions of source code must retain the above copyright 10142577Snjl * notice, this list of conditions and the following disclaimer. 11142577Snjl * 2. Redistributions in binary form must reproduce the above copyright 12142577Snjl * notice, this list of conditions and the following disclaimer in the 13142577Snjl * documentation and/or other materials provided with the distribution. 14142577Snjl * 15151461Snjl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR``AS IS'' AND ANY EXPRESS OR 16151461Snjl * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17142577Snjl * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18151461Snjl * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 19142577Snjl * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20142577Snjl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21142577Snjl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22142577Snjl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 23151461Snjl * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 24151461Snjl * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25142577Snjl * POSSIBILITY OF SUCH DAMAGE. 26142577Snjl */ 27142577Snjl 28142577Snjl#include <sys/cdefs.h> 29142577Snjl__FBSDID("$FreeBSD$"); 30142577Snjl 31149428Spjd#include <sys/param.h> 32149428Spjd#include <sys/ioctl.h> 33149428Spjd#include <sys/sysctl.h> 34149428Spjd#include <sys/resource.h> 35151461Snjl#include <sys/socket.h> 36153999Sdes#include <sys/time.h> 37151461Snjl#include <sys/un.h> 38149428Spjd 39142577Snjl#include <err.h> 40148139Sume#include <errno.h> 41142577Snjl#include <fcntl.h> 42149428Spjd#include <libutil.h> 43144873Snjl#include <signal.h> 44142577Snjl#include <stdio.h> 45142577Snjl#include <stdlib.h> 46142577Snjl#include <string.h> 47234070Srmh#include <sysexits.h> 48142577Snjl#include <unistd.h> 49142577Snjl 50203482Simp#ifdef __i386__ 51203482Simp#define USE_APM 52203482Simp#endif 53203482Simp 54153999Sdes#ifdef USE_APM 55142577Snjl#include <machine/apm_bios.h> 56142602Smarcel#endif 57142577Snjl 58185050Smav#define DEFAULT_ACTIVE_PERCENT 75 59185050Smav#define DEFAULT_IDLE_PERCENT 50 60185050Smav#define DEFAULT_POLL_INTERVAL 250 /* Poll interval in milliseconds */ 61142577Snjl 62153999Sdestypedef enum { 63142577Snjl MODE_MIN, 64142577Snjl MODE_ADAPTIVE, 65185050Smav MODE_HIADAPTIVE, 66142577Snjl MODE_MAX, 67153999Sdes} modes_t; 68142577Snjl 69153999Sdestypedef enum { 70142577Snjl SRC_AC, 71142577Snjl SRC_BATTERY, 72142577Snjl SRC_UNKNOWN, 73153999Sdes} power_src_t; 74142577Snjl 75228416Sedstatic const char *modes[] = { 76142577Snjl "AC", 77142577Snjl "battery", 78142577Snjl "unknown" 79142577Snjl}; 80142577Snjl 81142577Snjl#define ACPIAC "hw.acpi.acline" 82193161Snwhitehorn#define PMUAC "dev.pmu.0.acline" 83142577Snjl#define APMDEV "/dev/apm" 84151461Snjl#define DEVDPIPE "/var/run/devd.pipe" 85151461Snjl#define DEVCTL_MAXBUF 1024 86142577Snjl 87185050Smavstatic int read_usage_times(int *load); 88211415Sbrucecstatic int read_freqs(int *numfreqs, int **freqs, int **power, 89211415Sbrucec int minfreq, int maxfreq); 90142577Snjlstatic int set_freq(int freq); 91142605Snjlstatic void acline_init(void); 92153999Sdesstatic void acline_read(void); 93151461Snjlstatic int devd_init(void); 94151461Snjlstatic void devd_close(void); 95144873Snjlstatic void handle_sigs(int sig); 96142577Snjlstatic void parse_mode(char *arg, int *mode, int ch); 97142577Snjlstatic void usage(void); 98142577Snjl 99142577Snjl/* Sysctl data structures. */ 100185050Smavstatic int cp_times_mib[2]; 101142577Snjlstatic int freq_mib[4]; 102142577Snjlstatic int levels_mib[4]; 103193161Snwhitehornstatic int acline_mib[4]; 104193161Snwhitehornstatic size_t acline_mib_len; 105142577Snjl 106142577Snjl/* Configuration */ 107142577Snjlstatic int cpu_running_mark; 108142577Snjlstatic int cpu_idle_mark; 109142577Snjlstatic int poll_ival; 110151461Snjlstatic int vflag; 111142577Snjl 112153999Sdesstatic volatile sig_atomic_t exit_requested; 113153999Sdesstatic power_src_t acline_status; 114153999Sdesstatic enum { 115153999Sdes ac_none, 116193161Snwhitehorn ac_sysctl, 117153999Sdes ac_acpi_devd, 118153999Sdes#ifdef USE_APM 119153999Sdes ac_apm, 120153999Sdes#endif 121153999Sdes} acline_mode; 122153999Sdes#ifdef USE_APM 123153999Sdesstatic int apm_fd = -1; 124153999Sdes#endif 125153999Sdesstatic int devd_pipe = -1; 126142605Snjl 127153999Sdes#define DEVD_RETRY_INTERVAL 60 /* seconds */ 128153999Sdesstatic struct timeval tried_devd; 129153999Sdes 130142577Snjlstatic int 131185050Smavread_usage_times(int *load) 132142577Snjl{ 133185050Smav static long *cp_times = NULL, *cp_times_old = NULL; 134185050Smav static int ncpus = 0; 135185050Smav size_t cp_times_len; 136185050Smav int error, cpu, i, total; 137142577Snjl 138185050Smav if (cp_times == NULL) { 139185050Smav cp_times_len = 0; 140185050Smav error = sysctl(cp_times_mib, 2, NULL, &cp_times_len, NULL, 0); 141185050Smav if (error) 142185050Smav return (error); 143185050Smav if ((cp_times = malloc(cp_times_len)) == NULL) 144185050Smav return (errno); 145185050Smav if ((cp_times_old = malloc(cp_times_len)) == NULL) { 146185050Smav free(cp_times); 147185050Smav cp_times = NULL; 148185050Smav return (errno); 149185050Smav } 150185050Smav ncpus = cp_times_len / (sizeof(long) * CPUSTATES); 151185050Smav } 152185050Smav 153185050Smav cp_times_len = sizeof(long) * CPUSTATES * ncpus; 154185050Smav error = sysctl(cp_times_mib, 2, cp_times, &cp_times_len, NULL, 0); 155142577Snjl if (error) 156142577Snjl return (error); 157252713Swblock 158185050Smav if (load) { 159185050Smav *load = 0; 160185050Smav for (cpu = 0; cpu < ncpus; cpu++) { 161185050Smav total = 0; 162185050Smav for (i = 0; i < CPUSTATES; i++) { 163185050Smav total += cp_times[cpu * CPUSTATES + i] - 164185050Smav cp_times_old[cpu * CPUSTATES + i]; 165185050Smav } 166185050Smav if (total == 0) 167185050Smav continue; 168252713Swblock *load += 100 - (cp_times[cpu * CPUSTATES + CP_IDLE] - 169185050Smav cp_times_old[cpu * CPUSTATES + CP_IDLE]) * 100 / total; 170185050Smav } 171185050Smav } 172142577Snjl 173185050Smav memcpy(cp_times_old, cp_times, cp_times_len); 174142577Snjl 175142577Snjl return (0); 176142577Snjl} 177142577Snjl 178142577Snjlstatic int 179211415Sbrucecread_freqs(int *numfreqs, int **freqs, int **power, int minfreq, int maxfreq) 180142577Snjl{ 181142577Snjl char *freqstr, *p, *q; 182211415Sbrucec int i, j; 183142577Snjl size_t len = 0; 184142577Snjl 185142577Snjl if (sysctl(levels_mib, 4, NULL, &len, NULL, 0)) 186142577Snjl return (-1); 187142577Snjl if ((freqstr = malloc(len)) == NULL) 188142577Snjl return (-1); 189142577Snjl if (sysctl(levels_mib, 4, freqstr, &len, NULL, 0)) 190142577Snjl return (-1); 191142577Snjl 192142577Snjl *numfreqs = 1; 193142577Snjl for (p = freqstr; *p != '\0'; p++) 194142577Snjl if (*p == ' ') 195142577Snjl (*numfreqs)++; 196142577Snjl 197142577Snjl if ((*freqs = malloc(*numfreqs * sizeof(int))) == NULL) { 198142577Snjl free(freqstr); 199142577Snjl return (-1); 200142577Snjl } 201144873Snjl if ((*power = malloc(*numfreqs * sizeof(int))) == NULL) { 202144873Snjl free(freqstr); 203144873Snjl free(*freqs); 204144873Snjl return (-1); 205144873Snjl } 206211415Sbrucec for (i = 0, j = 0, p = freqstr; i < *numfreqs; i++) { 207142577Snjl q = strchr(p, ' '); 208142577Snjl if (q != NULL) 209142577Snjl *q = '\0'; 210211415Sbrucec if (sscanf(p, "%d/%d", &(*freqs)[j], &(*power)[i]) != 2) { 211142577Snjl free(freqstr); 212142577Snjl free(*freqs); 213144873Snjl free(*power); 214142577Snjl return (-1); 215142577Snjl } 216211415Sbrucec if (((*freqs)[j] >= minfreq || minfreq == -1) && 217211415Sbrucec ((*freqs)[j] <= maxfreq || maxfreq == -1)) 218211415Sbrucec j++; 219142577Snjl p = q + 1; 220142577Snjl } 221142577Snjl 222211415Sbrucec *numfreqs = j; 223211415Sbrucec if ((*freqs = realloc(*freqs, *numfreqs * sizeof(int))) == NULL) { 224211415Sbrucec free(freqstr); 225211415Sbrucec free(*freqs); 226211415Sbrucec free(*power); 227211415Sbrucec return (-1); 228211415Sbrucec } 229211415Sbrucec 230142577Snjl free(freqstr); 231142577Snjl return (0); 232142577Snjl} 233142577Snjl 234142577Snjlstatic int 235185050Smavget_freq(void) 236185050Smav{ 237185050Smav size_t len; 238185050Smav int curfreq; 239252713Swblock 240185050Smav len = sizeof(curfreq); 241185050Smav if (sysctl(freq_mib, 4, &curfreq, &len, NULL, 0) != 0) { 242185050Smav if (vflag) 243185050Smav warn("error reading current CPU frequency"); 244185050Smav curfreq = 0; 245185050Smav } 246185050Smav return (curfreq); 247185050Smav} 248185050Smav 249185050Smavstatic int 250142577Snjlset_freq(int freq) 251142577Snjl{ 252142577Snjl 253148139Sume if (sysctl(freq_mib, 4, NULL, NULL, &freq, sizeof(freq))) { 254148139Sume if (errno != EPERM) 255148139Sume return (-1); 256148139Sume } 257142577Snjl 258142577Snjl return (0); 259142577Snjl} 260142577Snjl 261185050Smavstatic int 262185050Smavget_freq_id(int freq, int *freqs, int numfreqs) 263185050Smav{ 264185050Smav int i = 1; 265252713Swblock 266185050Smav while (i < numfreqs) { 267185050Smav if (freqs[i] < freq) 268185050Smav break; 269185050Smav i++; 270185050Smav } 271185050Smav return (i - 1); 272185050Smav} 273185050Smav 274142605Snjl/* 275142605Snjl * Try to use ACPI to find the AC line status. If this fails, fall back 276153999Sdes * to APM. If nothing succeeds, we'll just run in default mode. 277142605Snjl */ 278142577Snjlstatic void 279201227Sedacline_init(void) 280142605Snjl{ 281193161Snwhitehorn acline_mib_len = 4; 282262474Sbrueffer acline_status = SRC_UNKNOWN; 283142605Snjl 284193161Snwhitehorn if (sysctlnametomib(ACPIAC, acline_mib, &acline_mib_len) == 0) { 285193161Snwhitehorn acline_mode = ac_sysctl; 286153999Sdes if (vflag) 287153999Sdes warnx("using sysctl for AC line status"); 288193161Snwhitehorn#if __powerpc__ 289193161Snwhitehorn } else if (sysctlnametomib(PMUAC, acline_mib, &acline_mib_len) == 0) { 290193161Snwhitehorn acline_mode = ac_sysctl; 291193161Snwhitehorn if (vflag) 292193161Snwhitehorn warnx("using sysctl for AC line status"); 293193161Snwhitehorn#endif 294153999Sdes#ifdef USE_APM 295153999Sdes } else if ((apm_fd = open(APMDEV, O_RDONLY)) >= 0) { 296153999Sdes if (vflag) 297153999Sdes warnx("using APM for AC line status"); 298153999Sdes acline_mode = ac_apm; 299153999Sdes#endif 300142605Snjl } else { 301153999Sdes warnx("unable to determine AC line status"); 302153999Sdes acline_mode = ac_none; 303142605Snjl } 304142605Snjl} 305142605Snjl 306153999Sdesstatic void 307153999Sdesacline_read(void) 308142605Snjl{ 309153999Sdes if (acline_mode == ac_acpi_devd) { 310153999Sdes char buf[DEVCTL_MAXBUF], *ptr; 311153999Sdes ssize_t rlen; 312153999Sdes int notify; 313142605Snjl 314153999Sdes rlen = read(devd_pipe, buf, sizeof(buf)); 315153999Sdes if (rlen == 0 || (rlen < 0 && errno != EWOULDBLOCK)) { 316153999Sdes if (vflag) 317153999Sdes warnx("lost devd connection, switching to sysctl"); 318153999Sdes devd_close(); 319193161Snwhitehorn acline_mode = ac_sysctl; 320153999Sdes /* FALLTHROUGH */ 321153999Sdes } 322153999Sdes if (rlen > 0 && 323153999Sdes (ptr = strstr(buf, "system=ACPI")) != NULL && 324153999Sdes (ptr = strstr(ptr, "subsystem=ACAD")) != NULL && 325153999Sdes (ptr = strstr(ptr, "notify=")) != NULL && 326153999Sdes sscanf(ptr, "notify=%x", ¬ify) == 1) 327153999Sdes acline_status = (notify ? SRC_AC : SRC_BATTERY); 328153999Sdes } 329193161Snwhitehorn if (acline_mode == ac_sysctl) { 330153999Sdes int acline; 331153999Sdes size_t len; 332151461Snjl 333153999Sdes len = sizeof(acline); 334193161Snwhitehorn if (sysctl(acline_mib, acline_mib_len, &acline, &len, 335193161Snwhitehorn NULL, 0) == 0) 336153999Sdes acline_status = (acline ? SRC_AC : SRC_BATTERY); 337153999Sdes else 338153999Sdes acline_status = SRC_UNKNOWN; 339153999Sdes } 340153999Sdes#ifdef USE_APM 341153999Sdes if (acline_mode == ac_apm) { 342153999Sdes struct apm_info info; 343153999Sdes 344153999Sdes if (ioctl(apm_fd, APMIO_GETINFO, &info) == 0) { 345153999Sdes acline_status = (info.ai_acline ? SRC_AC : SRC_BATTERY); 346153999Sdes } else { 347153999Sdes close(apm_fd); 348153999Sdes apm_fd = -1; 349153999Sdes acline_mode = ac_none; 350153999Sdes acline_status = SRC_UNKNOWN; 351153999Sdes } 352153999Sdes } 353142605Snjl#endif 354153999Sdes /* try to (re)connect to devd */ 355193161Snwhitehorn if (acline_mode == ac_sysctl) { 356153999Sdes struct timeval now; 357142605Snjl 358153999Sdes gettimeofday(&now, NULL); 359153999Sdes if (now.tv_sec > tried_devd.tv_sec + DEVD_RETRY_INTERVAL) { 360153999Sdes if (devd_init() >= 0) { 361153999Sdes if (vflag) 362153999Sdes warnx("using devd for AC line status"); 363153999Sdes acline_mode = ac_acpi_devd; 364153999Sdes } 365153999Sdes tried_devd = now; 366153999Sdes } 367153999Sdes } 368151461Snjl} 369151461Snjl 370151461Snjlstatic int 371151461Snjldevd_init(void) 372151461Snjl{ 373151461Snjl struct sockaddr_un devd_addr; 374151461Snjl 375151461Snjl bzero(&devd_addr, sizeof(devd_addr)); 376153999Sdes if ((devd_pipe = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) { 377151461Snjl if (vflag) 378153999Sdes warn("%s(): socket()", __func__); 379151461Snjl return (-1); 380142605Snjl } 381142605Snjl 382151461Snjl devd_addr.sun_family = PF_LOCAL; 383151461Snjl strlcpy(devd_addr.sun_path, DEVDPIPE, sizeof(devd_addr.sun_path)); 384153999Sdes if (connect(devd_pipe, (struct sockaddr *)&devd_addr, 385151461Snjl sizeof(devd_addr)) == -1) { 386153999Sdes if (vflag) 387153999Sdes warn("%s(): connect()", __func__); 388153999Sdes close(devd_pipe); 389153999Sdes devd_pipe = -1; 390151461Snjl return (-1); 391151461Snjl } 392151461Snjl 393153999Sdes if (fcntl(devd_pipe, F_SETFL, O_NONBLOCK) == -1) { 394153999Sdes if (vflag) 395153999Sdes warn("%s(): fcntl()", __func__); 396153999Sdes close(devd_pipe); 397153999Sdes return (-1); 398153999Sdes } 399153999Sdes 400153999Sdes return (devd_pipe); 401142605Snjl} 402142605Snjl 403142605Snjlstatic void 404151461Snjldevd_close(void) 405151461Snjl{ 406151461Snjl 407151461Snjl close(devd_pipe); 408153999Sdes devd_pipe = -1; 409151461Snjl} 410151461Snjl 411151461Snjlstatic void 412142577Snjlparse_mode(char *arg, int *mode, int ch) 413142577Snjl{ 414142577Snjl 415150564Sdes if (strcmp(arg, "minimum") == 0 || strcmp(arg, "min") == 0) 416142577Snjl *mode = MODE_MIN; 417150564Sdes else if (strcmp(arg, "maximum") == 0 || strcmp(arg, "max") == 0) 418142577Snjl *mode = MODE_MAX; 419179932Sobrien else if (strcmp(arg, "adaptive") == 0 || strcmp(arg, "adp") == 0) 420142577Snjl *mode = MODE_ADAPTIVE; 421185050Smav else if (strcmp(arg, "hiadaptive") == 0 || strcmp(arg, "hadp") == 0) 422185050Smav *mode = MODE_HIADAPTIVE; 423142577Snjl else 424142577Snjl errx(1, "bad option: -%c %s", (char)ch, optarg); 425142577Snjl} 426142577Snjl 427142577Snjlstatic void 428144873Snjlhandle_sigs(int __unused sig) 429144873Snjl{ 430151461Snjl 431144873Snjl exit_requested = 1; 432144873Snjl} 433144873Snjl 434144873Snjlstatic void 435142577Snjlusage(void) 436142577Snjl{ 437142577Snjl 438142577Snjl fprintf(stderr, 439211415Sbrucec"usage: powerd [-v] [-a mode] [-b mode] [-i %%] [-m freq] [-M freq] [-n mode] [-p ival] [-r %%] [-P pidfile]\n"); 440142577Snjl exit(1); 441142577Snjl} 442142577Snjl 443142577Snjlint 444142577Snjlmain(int argc, char * argv[]) 445142577Snjl{ 446153999Sdes struct timeval timeout; 447153999Sdes fd_set fdset; 448153999Sdes int nfds; 449149437Spjd struct pidfh *pfh = NULL; 450149428Spjd const char *pidfile = NULL; 451185053Smav int freq, curfreq, initfreq, *freqs, i, j, *mwatts, numfreqs, load; 452211415Sbrucec int minfreq = -1, maxfreq = -1; 453209234Smav int ch, mode, mode_ac, mode_battery, mode_none, idle, to; 454144873Snjl uint64_t mjoules_used; 455142577Snjl size_t len; 456142577Snjl 457142577Snjl /* Default mode for all AC states is adaptive. */ 458185050Smav mode_ac = mode_none = MODE_HIADAPTIVE; 459185050Smav mode_battery = MODE_ADAPTIVE; 460142577Snjl cpu_running_mark = DEFAULT_ACTIVE_PERCENT; 461142577Snjl cpu_idle_mark = DEFAULT_IDLE_PERCENT; 462142577Snjl poll_ival = DEFAULT_POLL_INTERVAL; 463144873Snjl mjoules_used = 0; 464142577Snjl vflag = 0; 465142577Snjl 466148395Snjl /* User must be root to control frequencies. */ 467148395Snjl if (geteuid() != 0) 468148395Snjl errx(1, "must be root to run"); 469148395Snjl 470211415Sbrucec while ((ch = getopt(argc, argv, "a:b:i:m:M:n:p:P:r:v")) != -1) 471142577Snjl switch (ch) { 472142577Snjl case 'a': 473142577Snjl parse_mode(optarg, &mode_ac, ch); 474142577Snjl break; 475142577Snjl case 'b': 476142577Snjl parse_mode(optarg, &mode_battery, ch); 477142577Snjl break; 478142577Snjl case 'i': 479142577Snjl cpu_idle_mark = atoi(optarg); 480142577Snjl if (cpu_idle_mark < 0 || cpu_idle_mark > 100) { 481142577Snjl warnx("%d is not a valid percent", 482142577Snjl cpu_idle_mark); 483142577Snjl usage(); 484142577Snjl } 485142577Snjl break; 486211415Sbrucec case 'm': 487211415Sbrucec minfreq = atoi(optarg); 488211415Sbrucec if (minfreq < 0) { 489211415Sbrucec warnx("%d is not a valid CPU frequency", 490211415Sbrucec minfreq); 491211415Sbrucec usage(); 492211415Sbrucec } 493211415Sbrucec break; 494211415Sbrucec case 'M': 495211415Sbrucec maxfreq = atoi(optarg); 496211415Sbrucec if (maxfreq < 0) { 497211415Sbrucec warnx("%d is not a valid CPU frequency", 498211415Sbrucec maxfreq); 499211415Sbrucec usage(); 500211415Sbrucec } 501211415Sbrucec break; 502142577Snjl case 'n': 503142577Snjl parse_mode(optarg, &mode_none, ch); 504142577Snjl break; 505142577Snjl case 'p': 506142577Snjl poll_ival = atoi(optarg); 507142577Snjl if (poll_ival < 5) { 508142577Snjl warnx("poll interval is in units of ms"); 509142577Snjl usage(); 510142577Snjl } 511142577Snjl break; 512149428Spjd case 'P': 513149428Spjd pidfile = optarg; 514149428Spjd break; 515142577Snjl case 'r': 516142577Snjl cpu_running_mark = atoi(optarg); 517185050Smav if (cpu_running_mark <= 0 || cpu_running_mark > 100) { 518142577Snjl warnx("%d is not a valid percent", 519142577Snjl cpu_running_mark); 520142577Snjl usage(); 521142577Snjl } 522142577Snjl break; 523142577Snjl case 'v': 524142577Snjl vflag = 1; 525142577Snjl break; 526142577Snjl default: 527142577Snjl usage(); 528142577Snjl } 529142577Snjl 530151461Snjl mode = mode_none; 531151461Snjl 532142577Snjl /* Poll interval is in units of ms. */ 533142577Snjl poll_ival *= 1000; 534142577Snjl 535142577Snjl /* Look up various sysctl MIBs. */ 536142577Snjl len = 2; 537185050Smav if (sysctlnametomib("kern.cp_times", cp_times_mib, &len)) 538185050Smav err(1, "lookup kern.cp_times"); 539142577Snjl len = 4; 540142577Snjl if (sysctlnametomib("dev.cpu.0.freq", freq_mib, &len)) 541234070Srmh err(EX_UNAVAILABLE, "no cpufreq(4) support -- aborting"); 542142577Snjl len = 4; 543142577Snjl if (sysctlnametomib("dev.cpu.0.freq_levels", levels_mib, &len)) 544142577Snjl err(1, "lookup freq_levels"); 545142577Snjl 546185050Smav /* Check if we can read the load and supported freqs. */ 547185050Smav if (read_usage_times(NULL)) 548142577Snjl err(1, "read_usage_times"); 549211415Sbrucec if (read_freqs(&numfreqs, &freqs, &mwatts, minfreq, maxfreq)) 550142577Snjl err(1, "error reading supported CPU frequencies"); 551211415Sbrucec if (numfreqs == 0) 552211415Sbrucec errx(1, "no CPU frequencies in user-specified range"); 553142577Snjl 554142577Snjl /* Run in the background unless in verbose mode. */ 555149428Spjd if (!vflag) { 556149428Spjd pid_t otherpid; 557149428Spjd 558149428Spjd pfh = pidfile_open(pidfile, 0600, &otherpid); 559149428Spjd if (pfh == NULL) { 560149428Spjd if (errno == EEXIST) { 561149428Spjd errx(1, "powerd already running, pid: %d", 562149428Spjd otherpid); 563149428Spjd } 564149428Spjd warn("cannot open pid file"); 565149428Spjd } 566151491Snjl if (daemon(0, 0) != 0) { 567151491Snjl warn("cannot enter daemon mode, exiting"); 568151491Snjl pidfile_remove(pfh); 569151491Snjl exit(EXIT_FAILURE); 570151491Snjl 571151491Snjl } 572149428Spjd pidfile_write(pfh); 573149428Spjd } 574142577Snjl 575151628Snjl /* Decide whether to use ACPI or APM to read the AC line status. */ 576151628Snjl acline_init(); 577151628Snjl 578153999Sdes /* 579153999Sdes * Exit cleanly on signals. 580153999Sdes */ 581153999Sdes signal(SIGINT, handle_sigs); 582153999Sdes signal(SIGTERM, handle_sigs); 583153999Sdes 584209234Smav freq = initfreq = curfreq = get_freq(); 585209234Smav i = get_freq_id(curfreq, freqs, numfreqs); 586185050Smav if (freq < 1) 587185050Smav freq = 1; 588211415Sbrucec 589211415Sbrucec /* 590211415Sbrucec * If we are in adaptive mode and the current frequency is outside the 591211415Sbrucec * user-defined range, adjust it to be within the user-defined range. 592211415Sbrucec */ 593211415Sbrucec acline_read(); 594211415Sbrucec if (acline_status > SRC_UNKNOWN) 595211415Sbrucec errx(1, "invalid AC line status %d", acline_status); 596211415Sbrucec if ((acline_status == SRC_AC && 597211415Sbrucec (mode_ac == MODE_ADAPTIVE || mode_ac == MODE_HIADAPTIVE)) || 598211415Sbrucec (acline_status == SRC_BATTERY && 599211415Sbrucec (mode_battery == MODE_ADAPTIVE || mode_battery == MODE_HIADAPTIVE)) || 600211415Sbrucec (acline_status == SRC_UNKNOWN && 601211415Sbrucec (mode_none == MODE_ADAPTIVE || mode_none == MODE_HIADAPTIVE))) { 602211415Sbrucec /* Read the current frequency. */ 603211415Sbrucec len = sizeof(curfreq); 604211415Sbrucec if (sysctl(freq_mib, 4, &curfreq, &len, NULL, 0) != 0) { 605211415Sbrucec if (vflag) 606211415Sbrucec warn("error reading current CPU frequency"); 607211415Sbrucec } 608211415Sbrucec if (curfreq < freqs[numfreqs - 1]) { 609211415Sbrucec if (vflag) { 610211415Sbrucec printf("CPU frequency is below user-defined " 611211415Sbrucec "minimum; changing frequency to %d " 612211415Sbrucec "MHz\n", freqs[numfreqs - 1]); 613211415Sbrucec } 614211415Sbrucec if (set_freq(freqs[numfreqs - 1]) != 0) { 615211415Sbrucec warn("error setting CPU freq %d", 616211415Sbrucec freqs[numfreqs - 1]); 617211415Sbrucec } 618211415Sbrucec } else if (curfreq > freqs[0]) { 619211415Sbrucec if (vflag) { 620211415Sbrucec printf("CPU frequency is above user-defined " 621211415Sbrucec "maximum; changing frequency to %d " 622211415Sbrucec "MHz\n", freqs[0]); 623211415Sbrucec } 624211415Sbrucec if (set_freq(freqs[0]) != 0) { 625211415Sbrucec warn("error setting CPU freq %d", 626211415Sbrucec freqs[0]); 627211415Sbrucec } 628211415Sbrucec } 629211415Sbrucec } 630211415Sbrucec 631209234Smav idle = 0; 632142577Snjl /* Main loop. */ 633142577Snjl for (;;) { 634153999Sdes FD_ZERO(&fdset); 635153999Sdes if (devd_pipe >= 0) { 636153999Sdes FD_SET(devd_pipe, &fdset); 637153999Sdes nfds = devd_pipe + 1; 638153999Sdes } else { 639153999Sdes nfds = 0; 640153999Sdes } 641209234Smav if (mode == MODE_HIADAPTIVE || idle < 120) 642209234Smav to = poll_ival; 643209234Smav else if (idle < 360) 644209234Smav to = poll_ival * 2; 645209234Smav else 646209234Smav to = poll_ival * 4; 647209234Smav timeout.tv_sec = to / 1000000; 648209234Smav timeout.tv_usec = to % 1000000; 649153999Sdes select(nfds, &fdset, NULL, &fdset, &timeout); 650142577Snjl 651144873Snjl /* If the user requested we quit, print some statistics. */ 652144873Snjl if (exit_requested) { 653144873Snjl if (vflag && mjoules_used != 0) 654144873Snjl printf("total joules used: %u.%03u\n", 655144873Snjl (u_int)(mjoules_used / 1000), 656144873Snjl (int)mjoules_used % 1000); 657144873Snjl break; 658144873Snjl } 659144873Snjl 660142577Snjl /* Read the current AC status and record the mode. */ 661153999Sdes acline_read(); 662153999Sdes switch (acline_status) { 663142577Snjl case SRC_AC: 664142577Snjl mode = mode_ac; 665142577Snjl break; 666142577Snjl case SRC_BATTERY: 667142577Snjl mode = mode_battery; 668142577Snjl break; 669142577Snjl case SRC_UNKNOWN: 670142577Snjl mode = mode_none; 671142577Snjl break; 672142577Snjl default: 673153999Sdes errx(1, "invalid AC line status %d", acline_status); 674142577Snjl } 675142577Snjl 676142577Snjl /* Read the current frequency. */ 677209234Smav if (idle % 32 == 0) { 678209234Smav if ((curfreq = get_freq()) == 0) 679209234Smav continue; 680209234Smav i = get_freq_id(curfreq, freqs, numfreqs); 681209234Smav } 682209234Smav idle++; 683144873Snjl if (vflag) { 684144873Snjl /* Keep a sum of all power actually used. */ 685185050Smav if (mwatts[i] != -1) 686144873Snjl mjoules_used += 687144873Snjl (mwatts[i] * (poll_ival / 1000)) / 1000; 688144873Snjl } 689144873Snjl 690142577Snjl /* Always switch to the lowest frequency in min mode. */ 691142577Snjl if (mode == MODE_MIN) { 692185050Smav freq = freqs[numfreqs - 1]; 693185050Smav if (curfreq != freq) { 694142577Snjl if (vflag) { 695142577Snjl printf("now operating on %s power; " 696142577Snjl "changing frequency to %d MHz\n", 697185050Smav modes[acline_status], freq); 698142577Snjl } 699209234Smav idle = 0; 700185050Smav if (set_freq(freq) != 0) { 701151461Snjl warn("error setting CPU freq %d", 702185050Smav freq); 703151461Snjl continue; 704151461Snjl } 705142577Snjl } 706142577Snjl continue; 707142577Snjl } 708142577Snjl 709142577Snjl /* Always switch to the highest frequency in max mode. */ 710142577Snjl if (mode == MODE_MAX) { 711185050Smav freq = freqs[0]; 712185050Smav if (curfreq != freq) { 713142577Snjl if (vflag) { 714142605Snjl printf("now operating on %s power; " 715142577Snjl "changing frequency to %d MHz\n", 716185050Smav modes[acline_status], freq); 717142577Snjl } 718209234Smav idle = 0; 719185050Smav if (set_freq(freq) != 0) { 720151461Snjl warn("error setting CPU freq %d", 721252713Swblock freq); 722151461Snjl continue; 723151461Snjl } 724142577Snjl } 725142577Snjl continue; 726142577Snjl } 727142577Snjl 728142577Snjl /* Adaptive mode; get the current CPU usage times. */ 729185050Smav if (read_usage_times(&load)) { 730151461Snjl if (vflag) 731151461Snjl warn("read_usage_times() failed"); 732151461Snjl continue; 733151461Snjl } 734252713Swblock 735185050Smav if (mode == MODE_ADAPTIVE) { 736185050Smav if (load > cpu_running_mark) { 737185050Smav if (load > 95 || load > cpu_running_mark * 2) 738185050Smav freq *= 2; 739185050Smav else 740185050Smav freq = freq * load / cpu_running_mark; 741185050Smav if (freq > freqs[0]) 742185050Smav freq = freqs[0]; 743185050Smav } else if (load < cpu_idle_mark && 744185050Smav curfreq * load < freqs[get_freq_id( 745252713Swblock freq * 7 / 8, freqs, numfreqs)] * 746185050Smav cpu_running_mark) { 747185050Smav freq = freq * 7 / 8; 748185050Smav if (freq < freqs[numfreqs - 1]) 749185050Smav freq = freqs[numfreqs - 1]; 750185050Smav } 751185050Smav } else { /* MODE_HIADAPTIVE */ 752185050Smav if (load > cpu_running_mark / 2) { 753185050Smav if (load > 95 || load > cpu_running_mark) 754185050Smav freq *= 4; 755185050Smav else 756185050Smav freq = freq * load * 2 / cpu_running_mark; 757185050Smav if (freq > freqs[0] * 2) 758185050Smav freq = freqs[0] * 2; 759185050Smav } else if (load < cpu_idle_mark / 2 && 760185050Smav curfreq * load < freqs[get_freq_id( 761252713Swblock freq * 31 / 32, freqs, numfreqs)] * 762185050Smav cpu_running_mark / 2) { 763185050Smav freq = freq * 31 / 32; 764185050Smav if (freq < freqs[numfreqs - 1]) 765185050Smav freq = freqs[numfreqs - 1]; 766185050Smav } 767149410Sbruno } 768185050Smav if (vflag) { 769185050Smav printf("load %3d%%, current freq %4d MHz (%2d), wanted freq %4d MHz\n", 770185050Smav load, curfreq, i, freq); 771185050Smav } 772185050Smav j = get_freq_id(freq, freqs, numfreqs); 773185050Smav if (i != j) { 774142577Snjl if (vflag) { 775185050Smav printf("changing clock" 776142577Snjl " speed from %d MHz to %d MHz\n", 777185050Smav freqs[i], freqs[j]); 778142577Snjl } 779209234Smav idle = 0; 780185050Smav if (set_freq(freqs[j])) 781170682Smarck warn("error setting CPU frequency %d", 782185050Smav freqs[j]); 783142577Snjl } 784142577Snjl } 785185053Smav if (set_freq(initfreq)) 786185053Smav warn("error setting CPU frequency %d", initfreq); 787144873Snjl free(freqs); 788144873Snjl free(mwatts); 789151461Snjl devd_close(); 790149428Spjd if (!vflag) 791149428Spjd pidfile_remove(pfh); 792142577Snjl 793142577Snjl exit(0); 794142577Snjl} 795