/*-
* 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 <sys/param.h>
#include <sys/ioctl.h>
#include <sys/sysctl.h>
#if __FreeBSD_version < 500101
#include <sys/dkstat.h>
#else
#include <sys/resource.h>
#endif
#include <machine/apm_bios.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#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;
}
syntax highlighted by Code2HTML, v. 0.9.1