15883360bSNate Lawson /*- 25883360bSNate Lawson * Copyright (c) 2004 Colin Percival 35883360bSNate Lawson * Copyright (c) 2005 Nate Lawson 45883360bSNate Lawson * All rights reserved. 55883360bSNate Lawson * 65883360bSNate Lawson * Redistribution and use in source and binary forms, with or without 75883360bSNate Lawson * modification, are permitted providing that the following conditions 85883360bSNate Lawson * are met: 95883360bSNate Lawson * 1. Redistributions of source code must retain the above copyright 105883360bSNate Lawson * notice, this list of conditions and the following disclaimer. 115883360bSNate Lawson * 2. Redistributions in binary form must reproduce the above copyright 125883360bSNate Lawson * notice, this list of conditions and the following disclaimer in the 135883360bSNate Lawson * documentation and/or other materials provided with the distribution. 145883360bSNate Lawson * 155883360bSNate Lawson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR``AS IS'' AND ANY EXPRESS OR 165883360bSNate Lawson * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 175883360bSNate Lawson * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 185883360bSNate Lawson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 195883360bSNate Lawson * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 205883360bSNate Lawson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 215883360bSNate Lawson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 225883360bSNate Lawson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 235883360bSNate Lawson * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 245883360bSNate Lawson * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 255883360bSNate Lawson * POSSIBILITY OF SUCH DAMAGE. 265883360bSNate Lawson */ 275883360bSNate Lawson 285883360bSNate Lawson #include <sys/cdefs.h> 295883360bSNate Lawson __FBSDID("$FreeBSD$"); 305883360bSNate Lawson 315883360bSNate Lawson #include <err.h> 325883360bSNate Lawson #include <fcntl.h> 335883360bSNate Lawson #include <stdio.h> 345883360bSNate Lawson #include <stdlib.h> 355883360bSNate Lawson #include <string.h> 365883360bSNate Lawson #include <unistd.h> 375883360bSNate Lawson 385883360bSNate Lawson #include <machine/apm_bios.h> 395883360bSNate Lawson 405883360bSNate Lawson #include <sys/ioctl.h> 415883360bSNate Lawson #include <sys/sysctl.h> 425883360bSNate Lawson #include <sys/resource.h> 435883360bSNate Lawson 445883360bSNate Lawson #define DEFAULT_ACTIVE_PERCENT 50 455883360bSNate Lawson #define DEFAULT_IDLE_PERCENT 75 465883360bSNate Lawson #define DEFAULT_POLL_INTERVAL 500 475883360bSNate Lawson 485883360bSNate Lawson enum modes_t { 495883360bSNate Lawson MODE_MIN, 505883360bSNate Lawson MODE_ADAPTIVE, 515883360bSNate Lawson MODE_MAX, 525883360bSNate Lawson }; 535883360bSNate Lawson 545883360bSNate Lawson enum power_src_t { 555883360bSNate Lawson SRC_AC, 565883360bSNate Lawson SRC_BATTERY, 575883360bSNate Lawson SRC_UNKNOWN, 585883360bSNate Lawson }; 595883360bSNate Lawson 605883360bSNate Lawson const char *modes[] = { 615883360bSNate Lawson "AC", 625883360bSNate Lawson "battery", 635883360bSNate Lawson "unknown" 645883360bSNate Lawson }; 655883360bSNate Lawson 665883360bSNate Lawson #define ACPIAC "hw.acpi.acline" 675883360bSNate Lawson #define APMDEV "/dev/apm" 685883360bSNate Lawson 695883360bSNate Lawson static int read_usage_times(long *idle, long *total); 705883360bSNate Lawson static int read_freqs(int *numfreqs, int **freqs); 715883360bSNate Lawson static int set_freq(int freq); 725883360bSNate Lawson static void parse_mode(char *arg, int *mode, int ch); 735883360bSNate Lawson static void usage(void); 745883360bSNate Lawson 755883360bSNate Lawson /* Sysctl data structures. */ 765883360bSNate Lawson static int cp_time_mib[2]; 775883360bSNate Lawson static int freq_mib[4]; 785883360bSNate Lawson static int levels_mib[4]; 795883360bSNate Lawson static int acline_mib[3]; 805883360bSNate Lawson 815883360bSNate Lawson /* Configuration */ 825883360bSNate Lawson static int cpu_running_mark; 835883360bSNate Lawson static int cpu_idle_mark; 845883360bSNate Lawson static int poll_ival; 855883360bSNate Lawson 865883360bSNate Lawson static int 875883360bSNate Lawson read_usage_times(long *idle, long *total) 885883360bSNate Lawson { 895883360bSNate Lawson static long idle_old, total_old; 905883360bSNate Lawson long cp_time[CPUSTATES], i, total_new; 915883360bSNate Lawson size_t cp_time_len; 925883360bSNate Lawson int error; 935883360bSNate Lawson 945883360bSNate Lawson cp_time_len = sizeof(cp_time); 955883360bSNate Lawson error = sysctl(cp_time_mib, 2, cp_time, &cp_time_len, NULL, 0); 965883360bSNate Lawson if (error) 975883360bSNate Lawson return (error); 985883360bSNate Lawson for (total_new = 0, i = 0; i < CPUSTATES; i++) 995883360bSNate Lawson total_new += cp_time[i]; 1005883360bSNate Lawson 1015883360bSNate Lawson if (idle) 1025883360bSNate Lawson *idle = cp_time[CP_IDLE] - idle_old; 1035883360bSNate Lawson if (total) 1045883360bSNate Lawson *total = total_new - total_old; 1055883360bSNate Lawson 1065883360bSNate Lawson idle_old = cp_time[CP_IDLE]; 1075883360bSNate Lawson total_old = total_new; 1085883360bSNate Lawson 1095883360bSNate Lawson return (0); 1105883360bSNate Lawson } 1115883360bSNate Lawson 1125883360bSNate Lawson static int 1135883360bSNate Lawson read_freqs(int *numfreqs, int **freqs) 1145883360bSNate Lawson { 1155883360bSNate Lawson char *freqstr, *p, *q; 1165883360bSNate Lawson int i; 1175883360bSNate Lawson size_t len = 0; 1185883360bSNate Lawson 1195883360bSNate Lawson if (sysctl(levels_mib, 4, NULL, &len, NULL, 0)) 1205883360bSNate Lawson return (-1); 1215883360bSNate Lawson if ((freqstr = malloc(len)) == NULL) 1225883360bSNate Lawson return (-1); 1235883360bSNate Lawson if (sysctl(levels_mib, 4, freqstr, &len, NULL, 0)) 1245883360bSNate Lawson return (-1); 1255883360bSNate Lawson 1265883360bSNate Lawson *numfreqs = 1; 1275883360bSNate Lawson for (p = freqstr; *p != '\0'; p++) 1285883360bSNate Lawson if (*p == ' ') 1295883360bSNate Lawson (*numfreqs)++; 1305883360bSNate Lawson 1315883360bSNate Lawson if ((*freqs = malloc(*numfreqs * sizeof(int))) == NULL) { 1325883360bSNate Lawson free(freqstr); 1335883360bSNate Lawson return (-1); 1345883360bSNate Lawson } 1355883360bSNate Lawson for (i = 0, p = freqstr; i < *numfreqs; i++) { 1365883360bSNate Lawson q = strchr(p, ' '); 1375883360bSNate Lawson if (q != NULL) 1385883360bSNate Lawson *q = '\0'; 1395883360bSNate Lawson if (sscanf(p, "%d/%*d", &(*freqs)[i]) != 1) { 1405883360bSNate Lawson free(freqstr); 1415883360bSNate Lawson free(*freqs); 1425883360bSNate Lawson return (-1); 1435883360bSNate Lawson } 1445883360bSNate Lawson p = q + 1; 1455883360bSNate Lawson } 1465883360bSNate Lawson 1475883360bSNate Lawson free(freqstr); 1485883360bSNate Lawson return (0); 1495883360bSNate Lawson } 1505883360bSNate Lawson 1515883360bSNate Lawson static int 1525883360bSNate Lawson set_freq(int freq) 1535883360bSNate Lawson { 1545883360bSNate Lawson 1555883360bSNate Lawson if (sysctl(freq_mib, 4, NULL, NULL, &freq, sizeof(freq))) 1565883360bSNate Lawson return (-1); 1575883360bSNate Lawson 1585883360bSNate Lawson return (0); 1595883360bSNate Lawson } 1605883360bSNate Lawson 1615883360bSNate Lawson static void 1625883360bSNate Lawson parse_mode(char *arg, int *mode, int ch) 1635883360bSNate Lawson { 1645883360bSNate Lawson 1655883360bSNate Lawson if (strcmp(arg, "min") == 0) 1665883360bSNate Lawson *mode = MODE_MIN; 1675883360bSNate Lawson else if (strcmp(arg, "max") == 0) 1685883360bSNate Lawson *mode = MODE_MAX; 1695883360bSNate Lawson else if (strcmp(arg, "adaptive") == 0) 1705883360bSNate Lawson *mode = MODE_ADAPTIVE; 1715883360bSNate Lawson else 1725883360bSNate Lawson errx(1, "bad option: -%c %s", (char)ch, optarg); 1735883360bSNate Lawson } 1745883360bSNate Lawson 1755883360bSNate Lawson static void 1765883360bSNate Lawson usage(void) 1775883360bSNate Lawson { 1785883360bSNate Lawson 1795883360bSNate Lawson fprintf(stderr, 1805883360bSNate Lawson "usage: powerd [-v] [-a mode] [-b mode] [-i %%] [-n mode] [-p ival] [-r %%]\n"); 1815883360bSNate Lawson exit(1); 1825883360bSNate Lawson } 1835883360bSNate Lawson 1845883360bSNate Lawson int 1855883360bSNate Lawson main(int argc, char * argv[]) 1865883360bSNate Lawson { 1875883360bSNate Lawson struct apm_info info; 1885883360bSNate Lawson long idle, total; 1895883360bSNate Lawson int apm_fd, curfreq, *freqs, i, numfreqs; 1905883360bSNate Lawson int ch, mode_ac, mode_battery, mode_none, acline, mode, vflag; 1915883360bSNate Lawson size_t len; 1925883360bSNate Lawson 1935883360bSNate Lawson /* Default mode for all AC states is adaptive. */ 1945883360bSNate Lawson mode_ac = mode_battery = mode_none = MODE_ADAPTIVE; 1955883360bSNate Lawson cpu_running_mark = DEFAULT_ACTIVE_PERCENT; 1965883360bSNate Lawson cpu_idle_mark = DEFAULT_IDLE_PERCENT; 1975883360bSNate Lawson poll_ival = DEFAULT_POLL_INTERVAL; 1985883360bSNate Lawson vflag = 0; 1995883360bSNate Lawson 2005883360bSNate Lawson while ((ch = getopt(argc, argv, "a:b:i:n:p:r:v")) != EOF) 2015883360bSNate Lawson switch (ch) { 2025883360bSNate Lawson case 'a': 2035883360bSNate Lawson parse_mode(optarg, &mode_ac, ch); 2045883360bSNate Lawson break; 2055883360bSNate Lawson case 'b': 2065883360bSNate Lawson parse_mode(optarg, &mode_battery, ch); 2075883360bSNate Lawson break; 2085883360bSNate Lawson case 'i': 2095883360bSNate Lawson cpu_idle_mark = atoi(optarg); 2105883360bSNate Lawson if (cpu_idle_mark < 0 || cpu_idle_mark > 100) { 2115883360bSNate Lawson warnx("%d is not a valid percent", 2125883360bSNate Lawson cpu_idle_mark); 2135883360bSNate Lawson usage(); 2145883360bSNate Lawson } 2155883360bSNate Lawson break; 2165883360bSNate Lawson case 'n': 2175883360bSNate Lawson parse_mode(optarg, &mode_none, ch); 2185883360bSNate Lawson break; 2195883360bSNate Lawson case 'p': 2205883360bSNate Lawson poll_ival = atoi(optarg); 2215883360bSNate Lawson if (poll_ival < 5) { 2225883360bSNate Lawson warnx("poll interval is in units of ms"); 2235883360bSNate Lawson usage(); 2245883360bSNate Lawson } 2255883360bSNate Lawson break; 2265883360bSNate Lawson case 'r': 2275883360bSNate Lawson cpu_running_mark = atoi(optarg); 2285883360bSNate Lawson if (cpu_running_mark < 0 || cpu_running_mark > 100) { 2295883360bSNate Lawson warnx("%d is not a valid percent", 2305883360bSNate Lawson cpu_running_mark); 2315883360bSNate Lawson usage(); 2325883360bSNate Lawson } 2335883360bSNate Lawson break; 2345883360bSNate Lawson case 'v': 2355883360bSNate Lawson vflag = 1; 2365883360bSNate Lawson break; 2375883360bSNate Lawson default: 2385883360bSNate Lawson usage(); 2395883360bSNate Lawson } 2405883360bSNate Lawson 2415883360bSNate Lawson /* Poll interval is in units of ms. */ 2425883360bSNate Lawson poll_ival *= 1000; 2435883360bSNate Lawson 2445883360bSNate Lawson /* Look up various sysctl MIBs. */ 2455883360bSNate Lawson len = 2; 2465883360bSNate Lawson if (sysctlnametomib("kern.cp_time", cp_time_mib, &len)) 2475883360bSNate Lawson err(1, "lookup kern.cp_time"); 2485883360bSNate Lawson len = 4; 2495883360bSNate Lawson if (sysctlnametomib("dev.cpu.0.freq", freq_mib, &len)) 2505883360bSNate Lawson err(1, "lookup freq"); 2515883360bSNate Lawson len = 4; 2525883360bSNate Lawson if (sysctlnametomib("dev.cpu.0.freq_levels", levels_mib, &len)) 2535883360bSNate Lawson err(1, "lookup freq_levels"); 2545883360bSNate Lawson 2555883360bSNate Lawson /* Check if we can read the idle time and supported freqs. */ 2565883360bSNate Lawson if (read_usage_times(NULL, NULL)) 2575883360bSNate Lawson err(1, "read_usage_times"); 2585883360bSNate Lawson if (read_freqs(&numfreqs, &freqs)) 2595883360bSNate Lawson err(1, "error reading supported CPU frequencies"); 2605883360bSNate Lawson 2615883360bSNate Lawson /* Decide whether to use ACPI or APM to read the AC line status. */ 2625883360bSNate Lawson len = sizeof(acline); 2635883360bSNate Lawson if (sysctlbyname(ACPIAC, &acline, &len, NULL, 0)) { 2645883360bSNate Lawson /* ACPI disabled, try APM */ 2655883360bSNate Lawson apm_fd = open(APMDEV, O_RDONLY); 2665883360bSNate Lawson if (apm_fd == -1) { 2675883360bSNate Lawson warnx("cannot read AC line status, " 2685883360bSNate Lawson "using default settings"); 2695883360bSNate Lawson } 2705883360bSNate Lawson } else { 2715883360bSNate Lawson len = 3; 2725883360bSNate Lawson if (sysctlnametomib(ACPIAC, acline_mib, &len)) 2735883360bSNate Lawson err(1, "lookup acline"); 2745883360bSNate Lawson apm_fd = -1; 2755883360bSNate Lawson } 2765883360bSNate Lawson 2775883360bSNate Lawson /* Run in the background unless in verbose mode. */ 2785883360bSNate Lawson if (!vflag) 2795883360bSNate Lawson daemon(0, 0); 2805883360bSNate Lawson 2815883360bSNate Lawson /* Main loop. */ 2825883360bSNate Lawson for (;;) { 2835883360bSNate Lawson /* Check status every few milliseconds. */ 2845883360bSNate Lawson usleep(poll_ival); 2855883360bSNate Lawson 2865883360bSNate Lawson /* Read the current AC status and record the mode. */ 2875883360bSNate Lawson if (apm_fd != -1) { 2885883360bSNate Lawson if (ioctl(apm_fd, APMIO_GETINFO, &info) == -1) 2895883360bSNate Lawson acline = SRC_UNKNOWN; 2905883360bSNate Lawson else 2915883360bSNate Lawson acline = info.ai_acline ? SRC_AC : SRC_BATTERY; 2925883360bSNate Lawson } else { 2935883360bSNate Lawson len = sizeof(acline); 2945883360bSNate Lawson if (sysctl(acline_mib, 3, &acline, &len, NULL, 0)) 2955883360bSNate Lawson acline = SRC_UNKNOWN; 2965883360bSNate Lawson else 2975883360bSNate Lawson acline = acline ? SRC_AC : SRC_BATTERY; 2985883360bSNate Lawson } 2995883360bSNate Lawson switch (acline) { 3005883360bSNate Lawson case SRC_AC: 3015883360bSNate Lawson mode = mode_ac; 3025883360bSNate Lawson break; 3035883360bSNate Lawson case SRC_BATTERY: 3045883360bSNate Lawson mode = mode_battery; 3055883360bSNate Lawson break; 3065883360bSNate Lawson case SRC_UNKNOWN: 3075883360bSNate Lawson mode = mode_none; 3085883360bSNate Lawson break; 3095883360bSNate Lawson default: 3105883360bSNate Lawson errx(1, "invalid AC line status %d", acline); 3115883360bSNate Lawson } 3125883360bSNate Lawson 3135883360bSNate Lawson /* Read the current frequency. */ 3145883360bSNate Lawson len = sizeof(curfreq); 3155883360bSNate Lawson if (sysctl(freq_mib, 4, &curfreq, &len, NULL, 0)) 3165883360bSNate Lawson err(1, "error reading current CPU frequency"); 3175883360bSNate Lawson 3185883360bSNate Lawson /* Always switch to the lowest frequency in min mode. */ 3195883360bSNate Lawson if (mode == MODE_MIN) { 3205883360bSNate Lawson if (curfreq != freqs[numfreqs - 1]) { 3215883360bSNate Lawson if (vflag) { 3225883360bSNate Lawson printf("now operating on %s power; " 3235883360bSNate Lawson "changing frequency to %d MHz\n", 3245883360bSNate Lawson modes[acline], freqs[numfreqs - 1]); 3255883360bSNate Lawson } 3265883360bSNate Lawson if (set_freq(freqs[numfreqs - 1])) 3275883360bSNate Lawson err(1, "error setting CPU freq %d", 3285883360bSNate Lawson freqs[numfreqs - 1]); 3295883360bSNate Lawson } 3305883360bSNate Lawson continue; 3315883360bSNate Lawson } 3325883360bSNate Lawson 3335883360bSNate Lawson /* Always switch to the highest frequency in max mode. */ 3345883360bSNate Lawson if (mode == MODE_MAX) { 3355883360bSNate Lawson if (curfreq != freqs[0]) { 3365883360bSNate Lawson if (vflag) { 3375883360bSNate Lawson printf("Now operating on %s power; " 3385883360bSNate Lawson "changing frequency to %d MHz\n", 3395883360bSNate Lawson modes[acline], freqs[0]); 3405883360bSNate Lawson } 3415883360bSNate Lawson if (set_freq(freqs[0])) 3425883360bSNate Lawson err(1, "error setting CPU freq %d", 3435883360bSNate Lawson freqs[0]); 3445883360bSNate Lawson } 3455883360bSNate Lawson continue; 3465883360bSNate Lawson } 3475883360bSNate Lawson 3485883360bSNate Lawson /* Adaptive mode; get the current CPU usage times. */ 3495883360bSNate Lawson if (read_usage_times(&idle, &total)) 3505883360bSNate Lawson err(1, "read_usage_times"); 3515883360bSNate Lawson 3525883360bSNate Lawson /* 3535883360bSNate Lawson * If we're idle less than the active mark, jump the CPU to 3545883360bSNate Lawson * its fastest speed if we're not there yet. If we're idle 3555883360bSNate Lawson * more than the idle mark, drop down to the first setting 3565883360bSNate Lawson * that is half the current speed (exponential backoff). 3575883360bSNate Lawson */ 3585883360bSNate Lawson if (idle < (total * cpu_running_mark) / 100 && 3595883360bSNate Lawson curfreq < freqs[0]) { 3605883360bSNate Lawson if (vflag) { 3615883360bSNate Lawson printf("idle time < %d%%, increasing clock" 3625883360bSNate Lawson " speed from %d MHz to %d MHz\n", 3635883360bSNate Lawson cpu_running_mark, curfreq, freqs[0]); 3645883360bSNate Lawson } 3655883360bSNate Lawson if (set_freq(freqs[0])) 3665883360bSNate Lawson err(1, "error setting CPU frequency %d", 3675883360bSNate Lawson freqs[0]); 3685883360bSNate Lawson } else if (idle > (total * cpu_idle_mark) / 100 && 3695883360bSNate Lawson curfreq > freqs[numfreqs - 1]) { 3705883360bSNate Lawson for (i = 0; i < numfreqs - 1; i++) { 3715883360bSNate Lawson if (freqs[i] <= curfreq / 2) 3725883360bSNate Lawson break; 3735883360bSNate Lawson } 3745883360bSNate Lawson if (vflag) { 3755883360bSNate Lawson printf("idle time > %d%%, decreasing clock" 3765883360bSNate Lawson " speed from %d MHz to %d MHz\n", 3775883360bSNate Lawson cpu_idle_mark, curfreq, freqs[i]); 3785883360bSNate Lawson } 3795883360bSNate Lawson if (set_freq(freqs[i])) 3805883360bSNate Lawson err(1, "error setting CPU frequency %d", 3815883360bSNate Lawson freqs[i]); 3825883360bSNate Lawson } 3835883360bSNate Lawson } 3845883360bSNate Lawson /* NOTREACHED */ 3855883360bSNate Lawson 3865883360bSNate Lawson if (apm_fd != -1) 3875883360bSNate Lawson close(apm_fd); 3885883360bSNate Lawson 3895883360bSNate Lawson exit(0); 3905883360bSNate Lawson } 391