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", &notify) == 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