/*- * Copyright 2004 Colin Percival. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted providing that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Colin Percival. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #if __FreeBSD_version < 500101 #include #else #include #endif #include #include #include #include #include #include #include #define MODE_MIN 0 #define MODE_ADAPTIVE 1 #define MODE_MAX 2 #define SRC_AC 0 #define SRC_BATTERY 1 #define SRC_UNKNOWN 2 #define ACPIAC "hw.acpi.acline" #define APMDEV "/dev/apm" static int readtimes(long * idle, long * total); static int readfreqs(int * numfreqs, int ** freqs); static int setcurfreq(int freq); static void readmode(char * arg, int * mode, int ch); static void usage(void); const char *modes[] = { "AC", "battery", "unknown" }; static int readtimes(long * idle, long * total) { static long idle_old, total_old; long cp_time[CPUSTATES]; size_t cp_time_len = sizeof(cp_time); long total_new, i; int error; if ((error = sysctlbyname("kern.cp_time", (void *)cp_time, &cp_time_len, NULL, 0)) != 0) return error; for (total_new = 0, i = 0; i < CPUSTATES; i++) total_new += cp_time[i]; if (idle) *idle = cp_time[CP_IDLE] - idle_old; if (total) *total = total_new - total_old; idle_old = cp_time[CP_IDLE]; total_old = total_new; return 0; } static int readfreqs(int * numfreqs, int ** freqs) { char *freqstr, *p, *q; int i; size_t len = 0; if (sysctlbyname("hw.est_freqs", NULL, &len, NULL, 0)) return -1; if ((freqstr = malloc(len)) == NULL) return -1; if (sysctlbyname("hw.est_freqs", (void *)freqstr, &len, NULL, 0)) return -1; *numfreqs = 1; for (p = freqstr; *p != '\0'; p++) if (*p == ' ') (*numfreqs)++; if ((*freqs = malloc(*numfreqs * sizeof(int))) == NULL) { free(freqstr); return -1; } for (i = 0, p = freqstr; i < *numfreqs; i++) { q = strchr(p, ' '); if (q != NULL) *q = '\0'; if (sscanf(p, "%d", &(*freqs)[i]) != 1) { free(freqstr); free(*freqs); return -1; } p = q + 1; } free(freqstr); return 0; } static int setcurfreq(int freq) { if (sysctlbyname("hw.est_curfreq", NULL, NULL, &freq, sizeof(freq))) return -1; return 0; } static void readmode(char * arg, int * mode, int ch) { if (strcmp(arg, "min") == 0) *mode = MODE_MIN; else if (strcmp(arg, "max") == 0) *mode = MODE_MAX; else if (strcmp(arg, "adaptive") == 0) *mode = MODE_ADAPTIVE; else errx(1, "Bad option: -%c %s", (char)ch, optarg); } static void usage(void) { (void)fprintf(stderr, "usage: estctrl [-v] [-a mode] [-b mode] " "[-d mode]\n"); exit(1); } int main(int argc, char * argv[]) { struct apm_info info; long idle, total; int * freqs, numfreqs; int curfreq, i; int ch, mode_ac, mode_battery, mode_default, acline, mode; int apm_fd; int vflag; size_t len; /* Default mode is adaptive */ mode_ac = mode_battery = mode_default = MODE_ADAPTIVE; vflag = 0; while ((ch = getopt(argc, argv, "a:b:d:v")) != -1) switch((char)ch) { case 'a': readmode(optarg, &mode_ac, ch); break; case 'b': readmode(optarg, &mode_battery, ch); break; case 'd': readmode(optarg, &mode_default, ch); break; case 'v': vflag = 1; break; default: usage(); } if (readtimes(NULL, NULL)) err(1, "readtimes"); if (readfreqs(&numfreqs, &freqs)) err(1, "Error reading supported CPU frequencies"); len = sizeof(acline); if (sysctlbyname(ACPIAC, &acline, &len, NULL, 0)) { /* ACPI disabled, try APM */ apm_fd = open(APMDEV, O_RDONLY); if (apm_fd == -1) warnx("Cannot read AC line status, " "using default settings"); } else apm_fd = -1; do { usleep(500000); /* Check status every 0.5 seconds */ if (apm_fd != -1) { if (ioctl(apm_fd, APMIO_GETINFO, &info) == -1) acline = SRC_UNKNOWN; else acline = info.ai_acline ? SRC_AC : SRC_BATTERY; } else { len = sizeof(acline); if (sysctlbyname(ACPIAC, &acline, &len, NULL, 0)) acline = SRC_UNKNOWN; else acline = acline ? SRC_AC : SRC_BATTERY; } switch(acline) { case SRC_AC: mode = mode_ac; break; case SRC_BATTERY: mode = mode_battery; break; case SRC_UNKNOWN: mode = mode_default; break; default: errx(1, "Programming error"); } len = sizeof(curfreq); if (sysctlbyname("hw.est_curfreq", &curfreq, &len, NULL, 0)) err(1, "Error reading current CPU frequency"); if (mode == MODE_MIN) { if (curfreq != freqs[0]) { if (vflag) printf("Now operating on %s power; " "changing frequency to %dMHz\n", modes[acline], freqs[0]); if (setcurfreq(freqs[0])) err(1, "Error setting CPU frequency"); } continue; } if (mode == MODE_MAX) { if (curfreq != freqs[numfreqs - 1]) { if (vflag) printf("Now operating on %s power; " "changing frequency to %dMHz\n", modes[acline], freqs[numfreqs - 1]); if (setcurfreq(freqs[numfreqs - 1])) err(1, "Error setting CPU frequency"); } continue; } if (readtimes(&idle, &total)) err(1, "readtimes"); if ((idle < total / 2) && (curfreq < freqs[numfreqs - 1])) { for (i = 0; i < numfreqs; i++) if (curfreq < freqs[i]) break; if (vflag) printf("Idle time < 50%%, increasing clock" " speed from %dMHz to %dMHz\n", curfreq, freqs[i]); if (setcurfreq(freqs[i])) err(1, "Error setting CPU frequency"); } if ((idle > (total * 3) / 4) && (curfreq > freqs[0])) { for (i = numfreqs - 1; i >= 0; i--) if (curfreq > freqs[i]) break; if (vflag) printf("Idle time > 75%%, decreasing clock" " speed from %dMHz to %dMHz\n", curfreq, freqs[i]); if (setcurfreq(freqs[i])) err(1, "Error setting CPU frequency"); } } while(1); /* NOTREACHED */ if (apm_fd != -1) close(apm_fd); return 0; }