1 /* 2 * Copyright (c) 2010 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 /* 36 * The powerd daemon monitors the cpu load and adjusts cpu frequencies 37 * via hw.acpi.cpu.px_dom*. 38 */ 39 40 #define _KERNEL_STRUCTURES 41 #include <sys/types.h> 42 #include <sys/sysctl.h> 43 #include <sys/kinfo.h> 44 #include <sys/file.h> 45 #include <sys/queue.h> 46 #include <sys/soundcard.h> 47 #include <sys/time.h> 48 #include <machine/cpufunc.h> 49 #include <err.h> 50 #include <stdio.h> 51 #include <stdlib.h> 52 #include <unistd.h> 53 #include <string.h> 54 #include <syslog.h> 55 56 #include "alert1.h" 57 58 #define MAXDOM MAXCPU /* worst case, 1 cpu per domain */ 59 60 #define MAXFREQ 64 61 62 struct cpu_pwrdom { 63 TAILQ_ENTRY(cpu_pwrdom) dom_link; 64 int dom_id; 65 int dom_ncpus; 66 cpumask_t dom_cpumask; 67 }; 68 TAILQ_HEAD(cpu_pwrdom_list, cpu_pwrdom); 69 70 static void usage(void); 71 static double getcputime(double); 72 static void acpi_setcpufreq(int nstate); 73 static int setupdominfo(void); 74 static int has_battery(void); 75 static int mon_battery(void); 76 static void getncpus(void); 77 78 static struct cpu_pwrdom_list CpuPwrDomain; 79 static struct cpu_pwrdom *CpuPwrDomLimit; 80 static struct cpu_pwrdom CpuPwrDomLast; 81 static int NCpuPwrDomUsed; 82 83 static int TotalCpus; 84 int DebugOpt; 85 int TurboOpt = 1; 86 int CpuLimit; /* # of cpus at max frequency */ 87 int PowerFd; 88 int NCpus; 89 int CpuCount[MAXDOM]; /* # of cpus in any given domain */ 90 int Hysteresis = 10; /* percentage */ 91 double TriggerUp = 0.25;/* single-cpu load to force max freq */ 92 double TriggerDown; /* load per cpu to force the min freq */ 93 static int BatLifeMin = 2; /* shutdown the box, if low on battery life */ 94 static struct timespec BatLifePrevT; 95 static int BatLifePollIntvl = 5; /* unit: sec */ 96 97 static struct timespec BatShutdownStartT; 98 static int BatShutdownLinger = -1; 99 static int BatShutdownLingerSet = 60; /* unit: sec */ 100 static int BatShutdownLingerCnt; 101 static int BatShutdownAudioAlert = 1; 102 103 static void sigintr(int signo); 104 105 int 106 main(int ac, char **av) 107 { 108 double qavg; 109 double uavg; /* uavg - used for speeding up */ 110 double davg; /* davg - used for slowing down */ 111 double srt; 112 double pollrate; 113 int ch; 114 int ustate; 115 int dstate; 116 int nstate; 117 char buf[64]; 118 int monbat; 119 120 srt = 8.0; /* time for samples - 8 seconds */ 121 pollrate = 1.0; /* polling rate in seconds */ 122 123 while ((ch = getopt(ac, av, "dp:r:tu:B:L:P:QT:")) != -1) { 124 switch(ch) { 125 case 'd': 126 DebugOpt = 1; 127 break; 128 case 'p': 129 Hysteresis = (int)strtol(optarg, NULL, 10); 130 break; 131 case 'r': 132 pollrate = strtod(optarg, NULL); 133 break; 134 case 't': 135 TurboOpt = 0; 136 break; 137 case 'u': 138 TriggerUp = (double)strtol(optarg, NULL, 10) / 100; 139 break; 140 case 'B': 141 BatLifeMin = strtol(optarg, NULL, 10); 142 break; 143 case 'L': 144 BatShutdownLingerSet = strtol(optarg, NULL, 10); 145 if (BatShutdownLingerSet < 0) 146 BatShutdownLingerSet = 0; 147 break; 148 case 'P': 149 BatLifePollIntvl = strtol(optarg, NULL, 10); 150 break; 151 case 'Q': 152 BatShutdownAudioAlert = 0; 153 break; 154 case 'T': 155 srt = strtod(optarg, NULL); 156 break; 157 default: 158 usage(); 159 /* NOT REACHED */ 160 } 161 } 162 ac -= optind; 163 av += optind; 164 165 /* Get the number of cpus */ 166 getncpus(); 167 168 if (0 > Hysteresis || Hysteresis > 99) { 169 fprintf(stderr, "Invalid hysteresis value\n"); 170 exit(1); 171 } 172 173 if (0 > TriggerUp || TriggerUp > 1) { 174 fprintf(stderr, "Invalid load limit value\n"); 175 exit(1); 176 } 177 178 TriggerDown = TriggerUp - (TriggerUp * (double) Hysteresis / 100); 179 180 /* 181 * Make sure powerd is not already running. 182 */ 183 PowerFd = open("/var/run/powerd.pid", O_CREAT|O_RDWR, 0644); 184 if (PowerFd < 0) { 185 fprintf(stderr, 186 "Cannot create /var/run/powerd.pid, " 187 "continuing anyway\n"); 188 } else { 189 if (flock(PowerFd, LOCK_EX|LOCK_NB) < 0) { 190 fprintf(stderr, "powerd is already running\n"); 191 exit(1); 192 } 193 } 194 195 /* 196 * Demonize and set pid 197 */ 198 if (DebugOpt == 0) { 199 daemon(0, 0); 200 openlog("powerd", LOG_CONS | LOG_PID, LOG_DAEMON); 201 } 202 203 if (PowerFd >= 0) { 204 ftruncate(PowerFd, 0); 205 snprintf(buf, sizeof(buf), "%d\n", (int)getpid()); 206 write(PowerFd, buf, strlen(buf)); 207 } 208 209 /* Do we need to monitor battery life? */ 210 if (BatLifePollIntvl <= 0) 211 monbat = 0; 212 else 213 monbat = has_battery(); 214 215 /* 216 * Wait hw.acpi.cpu.px_dom* sysctl to be created by kernel 217 * 218 * Since hw.acpi.cpu.px_dom* creation is queued into ACPI 219 * taskqueue and ACPI taskqueue is shared across various 220 * ACPI modules, any delay in other modules may cause 221 * hw.acpi.cpu.px_dom* to be created at quite a later time 222 * (e.g. cmbat module's task could take quite a lot of time). 223 */ 224 for (;;) { 225 /* 226 * Prime delta cputime calculation, make sure at least 227 * dom0 exists. 228 */ 229 getcputime(pollrate); 230 if (setupdominfo()) 231 break; 232 usleep((int)(pollrate * 1000000.0)); 233 } 234 235 /* 236 * Assume everything are used and are maxed out, before we 237 * start. 238 */ 239 CpuPwrDomLimit = &CpuPwrDomLast; 240 CpuLimit = NCpus; 241 242 /* 243 * Set to maximum performance if killed. 244 */ 245 signal(SIGINT, sigintr); 246 signal(SIGTERM, sigintr); 247 uavg = 0.0; 248 davg = 0.0; 249 250 srt = srt / pollrate; /* convert to sample count */ 251 252 if (DebugOpt) 253 printf("samples for downgrading: %5.2f\n", srt); 254 255 /* 256 * Monitoring loop 257 * 258 * Calculate nstate, the number of cpus we wish to run at max 259 * frequency. All remaining cpus will be set to their lowest 260 * frequency and mapped out of the user process scheduler. 261 */ 262 for (;;) { 263 qavg = getcputime(pollrate); 264 uavg = (uavg * 2.0 + qavg) / 3.0; /* speeding up */ 265 davg = (davg * srt + qavg) / (srt + 1); /* slowing down */ 266 if (davg < uavg) 267 davg = uavg; 268 269 ustate = uavg / TriggerUp; 270 if (ustate < CpuLimit) 271 ustate = uavg / TriggerDown; 272 dstate = davg / TriggerUp; 273 if (dstate < CpuLimit) 274 dstate = davg / TriggerDown; 275 276 nstate = (ustate > dstate) ? ustate : dstate; 277 if (nstate > NCpus) 278 nstate = NCpus; 279 280 if (DebugOpt) { 281 printf("\rqavg=%5.2f uavg=%5.2f davg=%5.2f " 282 "%2d/%2d ncpus=%d\r", 283 qavg, uavg, davg, 284 CpuLimit, NCpuPwrDomUsed, nstate); 285 fflush(stdout); 286 } 287 if (nstate != CpuLimit) 288 acpi_setcpufreq(nstate); 289 if (monbat) 290 monbat = mon_battery(); 291 usleep((int)(pollrate * 1000000.0)); 292 } 293 } 294 295 static 296 void 297 sigintr(int signo __unused) 298 { 299 syslog(LOG_INFO, "killed, setting max and exiting"); 300 acpi_setcpufreq(NCpus); 301 exit(1); 302 } 303 304 /* 305 * Figure out the domains and calculate the CpuCount[] array. 306 */ 307 static int 308 setupdominfo(void) 309 { 310 struct cpu_pwrdom *dom; 311 struct cpu_pwrdom_list tmp_list; 312 char buf[64]; 313 char members[1024]; 314 char *str; 315 size_t msize; 316 int n, i; 317 318 TAILQ_INIT(&CpuPwrDomain); 319 NCpuPwrDomUsed = 0; 320 NCpus = 0; 321 322 TAILQ_INIT(&tmp_list); 323 for (i = 0; i < MAXDOM; ++i) { 324 snprintf(buf, sizeof(buf), 325 "hw.acpi.cpu.px_dom%d.available", i); 326 if (sysctlbyname(buf, NULL, NULL, NULL, 0) < 0) 327 continue; 328 329 dom = calloc(1, sizeof(*dom)); 330 dom->dom_id = i; 331 TAILQ_INSERT_TAIL(&tmp_list, dom, dom_link); 332 } 333 334 while ((dom = TAILQ_FIRST(&tmp_list)) != NULL) { 335 int bsp_domain = 0; 336 337 TAILQ_REMOVE(&tmp_list, dom, dom_link); 338 CPUMASK_ASSZERO(dom->dom_cpumask); 339 340 snprintf(buf, sizeof(buf), 341 "hw.acpi.cpu.px_dom%d.members", dom->dom_id); 342 msize = sizeof(members); 343 if (sysctlbyname(buf, members, &msize, NULL, 0) < 0) { 344 free(dom); 345 continue; 346 } 347 348 members[msize] = 0; 349 for (str = strtok(members, " "); str; str = strtok(NULL, " ")) { 350 n = -1; 351 sscanf(str, "cpu%d", &n); 352 if (n >= 0) { 353 ++NCpus; 354 ++dom->dom_ncpus; 355 if (n == 0) 356 bsp_domain = 1; 357 CPUMASK_ORBIT(dom->dom_cpumask, n); 358 } 359 } 360 if (dom->dom_ncpus == 0) { 361 free(dom); 362 continue; 363 } 364 if (DebugOpt) { 365 printf("dom%d cpumask: ", dom->dom_id); 366 for (i = 0; i < (int)NELEM(dom->dom_cpumask.ary); ++i) { 367 printf("%jx ", 368 (uintmax_t)dom->dom_cpumask.ary[i]); 369 } 370 printf("\n"); 371 fflush(stdout); 372 } 373 374 if (bsp_domain) { 375 /* 376 * Use the power domain containing the BSP as the first 377 * power domain. So if all CPUs are idle, we could 378 * leave BSP to the usched without too much trouble. 379 */ 380 TAILQ_INSERT_HEAD(&CpuPwrDomain, dom, dom_link); 381 } else { 382 TAILQ_INSERT_TAIL(&CpuPwrDomain, dom, dom_link); 383 } 384 ++NCpuPwrDomUsed; 385 } 386 387 if (NCpus != TotalCpus) { 388 while ((dom = TAILQ_FIRST(&CpuPwrDomain)) != NULL) { 389 TAILQ_REMOVE(&CpuPwrDomain, dom, dom_link); 390 free(dom); 391 } 392 if (DebugOpt) { 393 printf("Found %d cpus, expecting %d\n", 394 NCpus, TotalCpus); 395 fflush(stdout); 396 } 397 return 0; 398 } 399 400 /* Install sentinel */ 401 CpuPwrDomLast.dom_id = -1; 402 TAILQ_INSERT_TAIL(&CpuPwrDomain, &CpuPwrDomLast, dom_link); 403 404 return 1; 405 } 406 407 /* 408 * Return the one-second cpu load. One cpu at 100% will return a value 409 * of 1.0. On a SMP system N cpus running at 100% will return a value of N. 410 */ 411 static 412 double 413 getcputime(double pollrate) 414 { 415 static struct kinfo_cputime ocpu_time[MAXCPU]; 416 static struct kinfo_cputime ncpu_time[MAXCPU]; 417 size_t slen; 418 int ncpu; 419 int cpu; 420 uint64_t delta; 421 422 /* NOTE: Don't use NCpus here; it may not be initialized yet */ 423 bcopy(ncpu_time, ocpu_time, sizeof(struct kinfo_cputime) * TotalCpus); 424 425 slen = sizeof(ncpu_time); 426 if (sysctlbyname("kern.cputime", &ncpu_time, &slen, NULL, 0) < 0) { 427 fprintf(stderr, "kern.cputime sysctl not available\n"); 428 exit(1); 429 } 430 ncpu = slen / sizeof(ncpu_time[0]); 431 432 delta = 0; 433 for (cpu = 0; cpu < ncpu; ++cpu) { 434 delta += (ncpu_time[cpu].cp_user + ncpu_time[cpu].cp_sys + 435 ncpu_time[cpu].cp_nice + ncpu_time[cpu].cp_intr) - 436 (ocpu_time[cpu].cp_user + ocpu_time[cpu].cp_sys + 437 ocpu_time[cpu].cp_nice + ocpu_time[cpu].cp_intr); 438 } 439 return((double)delta / (pollrate * 1000000.0)); 440 } 441 442 static void 443 acpi_getcpufreq_str(int dom_id, int *highest0, int *lowest0) 444 { 445 char buf[256], sysid[64]; 446 size_t buflen; 447 char *ptr; 448 int v, highest, lowest; 449 450 /* 451 * Retrieve availability list 452 */ 453 snprintf(sysid, sizeof(sysid), "hw.acpi.cpu.px_dom%d.available", 454 dom_id); 455 buflen = sizeof(buf) - 1; 456 if (sysctlbyname(sysid, buf, &buflen, NULL, 0) < 0) 457 return; 458 buf[buflen] = 0; 459 460 /* 461 * Parse out the highest and lowest cpu frequencies 462 */ 463 ptr = buf; 464 highest = lowest = 0; 465 while (ptr && (v = strtol(ptr, &ptr, 10)) > 0) { 466 if (lowest == 0 || lowest > v) 467 lowest = v; 468 if (highest == 0 || highest < v) 469 highest = v; 470 /* 471 * Detect turbo mode 472 */ 473 if (!TurboOpt && highest - v == 1) 474 highest = v; 475 } 476 477 *highest0 = highest; 478 *lowest0 = lowest; 479 } 480 481 static int 482 acpi_getcpufreq_bin(int dom_id, int *highest0, int *lowest0) 483 { 484 char sysid[64]; 485 int freq[MAXFREQ]; 486 size_t freqlen; 487 int freqcnt; 488 489 /* 490 * Retrieve availability list 491 */ 492 snprintf(sysid, sizeof(sysid), "hw.acpi.cpu.px_dom%d.available_bin", 493 dom_id); 494 freqlen = sizeof(freq); 495 if (sysctlbyname(sysid, freq, &freqlen, NULL, 0) < 0) 496 return 0; 497 498 freqcnt = freqlen / sizeof(freq[0]); 499 if (freqcnt == 0) 500 return 0; 501 502 *lowest0 = freq[freqcnt - 1]; 503 504 *highest0 = freq[0]; 505 if (!TurboOpt && freqcnt > 1 && freq[0] - freq[1] == 1) 506 *highest0 = freq[1]; 507 return 1; 508 } 509 510 static void 511 acpi_getcpufreq(int dom_id, int *highest, int *lowest) 512 { 513 *highest = 0; 514 *lowest = 0; 515 516 if (acpi_getcpufreq_bin(dom_id, highest, lowest)) 517 return; 518 acpi_getcpufreq_str(dom_id, highest, lowest); 519 } 520 521 /* 522 * nstate is the requested number of cpus that we wish to run at full 523 * frequency. We calculate how many domains we have to adjust to reach 524 * this goal. 525 * 526 * This function also sets the user scheduler global cpu mask. 527 */ 528 static void 529 acpi_setcpufreq(int nstate) 530 { 531 int ncpus = 0; 532 int increasing = (nstate > CpuLimit); 533 struct cpu_pwrdom *dom, *domBeg, *domEnd; 534 int lowest; 535 int highest; 536 int desired; 537 char sysid[64]; 538 cpumask_t global_cpumask; 539 540 /* 541 * Calculate the ending domain if the number of operating cpus 542 * has increased. 543 * 544 * Calculate the starting domain if the number of operating cpus 545 * has decreased. 546 * 547 * Calculate the mask of cpus the userland scheduler is allowed 548 * to use. 549 */ 550 NCpuPwrDomUsed = 0; 551 CPUMASK_ASSZERO(global_cpumask); 552 for (dom = TAILQ_FIRST(&CpuPwrDomain); dom != &CpuPwrDomLast; 553 dom = TAILQ_NEXT(dom, dom_link)) { 554 cpumask_t mask; 555 556 if (ncpus >= nstate) 557 break; 558 ncpus += dom->dom_ncpus; 559 ++NCpuPwrDomUsed; 560 561 mask = dom->dom_cpumask; 562 if (ncpus > nstate) { 563 int i, diff; 564 565 diff = ncpus - nstate; 566 for (i = 0; i < diff; ++i) { 567 int c; 568 569 c = BSRCPUMASK(mask); 570 CPUMASK_NANDBIT(mask, c); 571 } 572 } 573 CPUMASK_ORMASK(global_cpumask, mask); 574 } 575 576 /* 577 * Make sure that userland scheduler has at least one cpu. 578 */ 579 if (CPUMASK_TESTZERO(global_cpumask)) 580 CPUMASK_ORBIT(global_cpumask, 0); 581 if (DebugOpt) { 582 int i; 583 584 printf("\nusched cpumask: "); 585 for (i = 0; i < (int)NELEM(global_cpumask.ary); ++i) 586 printf("%jx ", (uintmax_t)global_cpumask.ary[i]); 587 printf("\n"); 588 fflush(stdout); 589 } 590 591 syslog(LOG_INFO, "using %d cpus", nstate); 592 593 /* 594 * Set the mask of cpus the userland scheduler is allowed to use. 595 */ 596 sysctlbyname("kern.usched_global_cpumask", NULL, 0, 597 &global_cpumask, sizeof(global_cpumask)); 598 599 if (increasing) { 600 domBeg = CpuPwrDomLimit; 601 domEnd = dom; 602 } else { 603 domBeg = dom; 604 domEnd = CpuPwrDomLimit; 605 } 606 CpuPwrDomLimit = dom; 607 CpuLimit = nstate; 608 609 /* 610 * Adjust the cpu frequency 611 */ 612 for (dom = domBeg; dom != domEnd; dom = TAILQ_NEXT(dom, dom_link)) { 613 acpi_getcpufreq(dom->dom_id, &highest, &lowest); 614 if (highest == 0 || lowest == 0) 615 continue; 616 617 /* 618 * Calculate the desired cpu frequency, test, and set. 619 */ 620 desired = increasing ? highest : lowest; 621 622 snprintf(sysid, sizeof(sysid), "hw.acpi.cpu.px_dom%d.select", 623 dom->dom_id); 624 if (DebugOpt) { 625 printf("dom%d set frequency %d\n", 626 dom->dom_id, desired); 627 } 628 sysctlbyname(sysid, NULL, NULL, &desired, sizeof(desired)); 629 } 630 } 631 632 static 633 void 634 usage(void) 635 { 636 fprintf(stderr, "usage: powerd [-dt] [-p hysteresis] " 637 "[-u trigger_up] [-T sample_interval] [-r poll_interval] " 638 "[-B min_battery_life] [-L low_battery_linger] " 639 "[-P battery_poll_interval] [-Q]\n"); 640 exit(1); 641 } 642 643 #ifndef timespecsub 644 #define timespecsub(vvp, uvp) \ 645 do { \ 646 (vvp)->tv_sec -= (uvp)->tv_sec; \ 647 (vvp)->tv_nsec -= (uvp)->tv_nsec; \ 648 if ((vvp)->tv_nsec < 0) { \ 649 (vvp)->tv_sec--; \ 650 (vvp)->tv_nsec += 1000000000; \ 651 } \ 652 } while (0) 653 #endif 654 655 #define BAT_SYSCTL_TIME_MAX 50000000 /* unit: nanosecond */ 656 657 static int 658 has_battery(void) 659 { 660 struct timespec s, e; 661 size_t len; 662 int val; 663 664 clock_gettime(CLOCK_MONOTONIC_FAST, &s); 665 BatLifePrevT = s; 666 667 len = sizeof(val); 668 if (sysctlbyname("hw.acpi.acline", &val, &len, NULL, 0) < 0) { 669 /* No AC line information */ 670 return 0; 671 } 672 clock_gettime(CLOCK_MONOTONIC_FAST, &e); 673 674 timespecsub(&e, &s); 675 if (e.tv_sec > 0 || e.tv_nsec > BAT_SYSCTL_TIME_MAX) { 676 /* hw.acpi.acline takes to long to be useful */ 677 syslog(LOG_NOTICE, "hw.acpi.acline takes too long"); 678 return 0; 679 } 680 681 clock_gettime(CLOCK_MONOTONIC_FAST, &s); 682 len = sizeof(val); 683 if (sysctlbyname("hw.acpi.battery.life", &val, &len, NULL, 0) < 0) { 684 /* No battery life */ 685 return 0; 686 } 687 clock_gettime(CLOCK_MONOTONIC_FAST, &e); 688 689 timespecsub(&e, &s); 690 if (e.tv_sec > 0 || e.tv_nsec > BAT_SYSCTL_TIME_MAX) { 691 /* hw.acpi.battery.life takes to long to be useful */ 692 syslog(LOG_NOTICE, "hw.acpi.battery.life takes too long"); 693 return 0; 694 } 695 return 1; 696 } 697 698 static void 699 low_battery_alert(int life) 700 { 701 int fmt, stereo, freq; 702 int fd; 703 704 syslog(LOG_ALERT, "low battery life %d%%, please plugin AC line, #%d", 705 life, BatShutdownLingerCnt); 706 ++BatShutdownLingerCnt; 707 708 if (!BatShutdownAudioAlert) 709 return; 710 711 fd = open("/dev/dsp", O_WRONLY); 712 if (fd < 0) 713 return; 714 715 fmt = AFMT_S16_LE; 716 if (ioctl(fd, SNDCTL_DSP_SETFMT, &fmt, sizeof(fmt)) < 0) 717 goto done; 718 719 stereo = 0; 720 if (ioctl(fd, SNDCTL_DSP_STEREO, &stereo, sizeof(stereo)) < 0) 721 goto done; 722 723 freq = 44100; 724 if (ioctl(fd, SNDCTL_DSP_SPEED, &freq, sizeof(freq)) < 0) 725 goto done; 726 727 write(fd, alert1, sizeof(alert1)); 728 write(fd, alert1, sizeof(alert1)); 729 730 done: 731 close(fd); 732 } 733 734 static int 735 mon_battery(void) 736 { 737 struct timespec cur, ts; 738 int acline, life; 739 size_t len; 740 741 clock_gettime(CLOCK_MONOTONIC_FAST, &cur); 742 ts = cur; 743 timespecsub(&ts, &BatLifePrevT); 744 if (ts.tv_sec < BatLifePollIntvl) 745 return 1; 746 BatLifePrevT = cur; 747 748 len = sizeof(acline); 749 if (sysctlbyname("hw.acpi.acline", &acline, &len, NULL, 0) < 0) 750 return 1; 751 if (acline) { 752 BatShutdownLinger = -1; 753 BatShutdownLingerCnt = 0; 754 return 1; 755 } 756 757 len = sizeof(life); 758 if (sysctlbyname("hw.acpi.battery.life", &life, &len, NULL, 0) < 0) 759 return 1; 760 761 if (BatShutdownLinger > 0) { 762 ts = cur; 763 timespecsub(&ts, &BatShutdownStartT); 764 if (ts.tv_sec > BatShutdownLinger) 765 BatShutdownLinger = 0; 766 } 767 768 if (life <= BatLifeMin) { 769 if (BatShutdownLinger == 0 || BatShutdownLingerSet == 0) { 770 syslog(LOG_ALERT, "low battery life %d%%, " 771 "shutting down", life); 772 if (vfork() == 0) 773 execlp("poweroff", "poweroff", NULL); 774 return 0; 775 } else if (BatShutdownLinger < 0) { 776 BatShutdownLinger = BatShutdownLingerSet; 777 BatShutdownStartT = cur; 778 } 779 low_battery_alert(life); 780 } 781 return 1; 782 } 783 784 static void 785 getncpus(void) 786 { 787 size_t slen; 788 789 slen = sizeof(TotalCpus); 790 if (sysctlbyname("hw.ncpu", &TotalCpus, &slen, NULL, 0) < 0) 791 err(1, "sysctlbyname hw.ncpu failed"); 792 if (DebugOpt) 793 printf("hw.ncpu %d\n", TotalCpus); 794 } 795