/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Battery control/ACPI interface. */ #pragma ident "@(#)batstat.c 1.16 05/10/21 SMI" #include #include #include #include #include #include #include #include #include #include "acpidrv.h" #define DEG(k) (((k) - 2731)/10) #define BATSTAT_INSTANCE "svc:/site/batstat:default" static void property_getval(const char *name, int *res) { scf_simple_prop_t *prop; int64_t *vp; prop = scf_simple_prop_get(NULL, BATSTAT_INSTANCE, "options", name); if (prop == NULL) return; vp = scf_simple_prop_next_integer(prop); if (vp == NULL) { scf_simple_prop_free(prop); return; } *res = (int)*vp; scf_simple_prop_free(prop); } static int lowthresh = 2; static int shutthresh = 5; static int enabled = 0; static struct param { const char *name; int *valp; } params[] = { { "poweroff", &lowthresh }, { "shutdown", &shutthresh }, { "enabled", &enabled }, }; #define NPARAM (sizeof (params) / sizeof (struct param)) static int dumptemp(int fd) { int ntz; int i; acpi_thermal_t tht[ACPI_THERMALZONE_MAX]; if (ioctl(fd, ACPI_THERMALZONE_COUNT, &ntz) != 0) { perror("ioctl(ACPI_THERMALZONE_COUNT)"); return (1); } if (ntz == 0) { (void) fprintf(stderr, "No thermal zones\n"); return (1); } if (ioctl(fd, ACPI_THERMALZONE_INFO, tht) != 0) { perror("ioctl(ACPI_THERMALZONE_INFO)"); return (1); } for (i = 0; i < ntz; i++) { (void) printf("Thermal zone: %s, temperature = %dC, " "critical = %dC\n", tht[i].name, DEG(tht[i].tmp), DEG(tht[i].crit)); if (tht[i].nac > 0) { int j; (void) printf("\tActive Cooling:"); for (j = 0; j < tht[i].nac; j++) { if (tht[i].ac[j] > 0) (void) printf(" %d", DEG(tht[i].ac[j])); } (void) putchar('\n'); } } return (0); } static int showwarnstat(int fd) { battery_warning_t bwarn; if (ioctl(fd, ACPI_BATTERY_GET_WARNING, &bwarn) != 0) { perror("ioctl(ACPI_BATTERY_GET_WARNING)"); return (1); } (void) printf("Battery low shutdown %s\n", bwarn.bw_enabled ? "enabled" : "disabled"); if (bwarn.bw_enabled == 0) return (0); (void) printf("\tBattery emergency power off at %d%%\n", bwarn.bw_poweroff); (void) printf("\tBattery low ordinary shutdown at %d%%\n", bwarn.bw_shutdown); return (0); } static int setwarnstat(int fd, int warn, int pow, int shut) { battery_warning_t bwarn; if (ioctl(fd, ACPI_BATTERY_GET_WARNING, &bwarn) != 0) { perror("ioctl(ACPI_BATTERY_GET_WARNING)"); return (1); } bwarn.bw_enabled = warn; if (warn) { if (shut >= 0) bwarn.bw_shutdown = shut; if (pow >= 0) bwarn.bw_poweroff = pow; } if (ioctl(fd, ACPI_BATTERY_SET_WARNING, &bwarn) != 0) { perror("ioctl(ACPI_BATTERY_SET_WARNING)"); return (1); } return (0); } static void batwarn_init(int fd) { int i; for (i = 0; i < NPARAM; i++) property_getval(params[i].name, params[i].valp); (void) setwarnstat(fd, enabled, lowthresh, shutthresh); exit(0); } void usage(int interval) { fprintf(stderr, "Options:\n"); fprintf(stderr, " -v dump raw output\n"); fprintf(stderr, " -x xtitle output (implies looping)\n"); fprintf(stderr, " -l loop\n"); fprintf(stderr, " -i sec number of seconds for each loop (default is %d)\n", interval); fprintf(stderr, " -p use poll on acpidrv\n"); fprintf(stderr, " -d # ACPI VIDEO switch ????\n"); fprintf(stderr, " -s display battery warning status\n"); fprintf(stderr, " -t display thermal status\n"); fprintf(stderr, " -w enable battery warning\n"); fprintf(stderr, " -W disable battery warning\n"); fprintf(stderr, " -I initialize the default batstat parameters for low battery shutdown\n"); fprintf(stderr, " -P # set emergency poweroff threshold\n"); fprintf(stderr, " -S # set shutdown threshold\n"); } int main(int argc, char **argv) { int fd = open("/dev/acpidrv", O_RDWR); acpi_bif_t *bif; acpi_bst_t *bst; int hours; int minutes; int nbat; int i; int pow; int verbose = 0; int x = 0; int loop = 0; int c; int interval = 60; int temp; int tverbose = 0; int dos = -1; boolean_t pollit = B_FALSE; struct pollfd xfd; int warn = -1; boolean_t warnstat = B_FALSE; int shutdown = -1; int poweroff = -1; while ((c = getopt(argc, argv, "pvxli:d:twWsP:S:I")) != EOF) { switch (c) { case 'v': verbose = 1; break; case 'x': x = 1; /* FALLTHROUGH */ case 'p': pollit = B_TRUE; /* FALLTHROUGH */ case 'l': loop = 1; break; case 'i': interval = atoi(optarg); break; case 'd': dos = atoi(optarg); break; case 't': tverbose = 1; break; case 'w': warn = 1; break; case 'W': warn = 0; break; case 's': warnstat = B_TRUE; break; case 'P': poweroff = atoi(optarg); break; case 'S': shutdown = atoi(optarg); break; case 'I': batwarn_init(fd); break; default: usage(interval); exit(1); } } if (fd < 0) { perror("/dev/apcidrv"); (void) close(fd); exit(1); } if (dos != -1) { (void) ioctl(fd, ACPI_VIDEO_SWITCH, dos); exit(0); } if (warnstat) exit(showwarnstat(fd)); if (warn != -1) exit(setwarnstat(fd, warn, poweroff, shutdown)); if (tverbose) exit(dumptemp(fd)); if (ioctl(fd, ACPI_BATTERY_COUNT, &nbat) != 0 || nbat == 0) { (void) fprintf(stderr, "No battery information available.\n"); exit(1); } bif = malloc(sizeof (*bif) * nbat); bst = malloc(sizeof (*bst) * nbat); loop: if (ioctl(fd, ACPI_BATTERY_INFO, bif) != 0) { perror("ACPI_BATTERY_INFO"); exit(1); } if (ioctl(fd, ACPI_POWER_STATUS, &pow) != 0) { perror("ACPI_POWER_STATUS"); exit(1); } if (ioctl(fd, ACPI_TEMPERATURE, &temp) != 0) temp = -1; if (pow && !x) (void) printf("System is currently on AC power\n"); if (temp >= 0 && !x) { int frac; temp -= 2732; frac = temp % 10; if (frac == 9) temp++; (void) printf("System temperature = %d.%dC\n", temp/10, temp % 10); } if (verbose) { float f; for (i = 0; i < nbat; i++) { (void) printf("battery %d\n", i); (void) printf("\tbif_unit = %x\n", bif[i].bif_unit); (void) printf("\tbif_design_cap = %4x %2u.%3.3u Wh\n", bif[i].bif_design_cap, bif[i].bif_design_cap / 1000, bif[i].bif_design_cap % 1000); if (bif[i].bif_design_cap == 0) f = 0.0; else f = bif[i].bif_last_cap * 100.0 / bif[i].bif_design_cap; (void) printf("\tbif_last_cap = %4x %2u.%3.3u Wh (%4.2f%%)\n", bif[i].bif_last_cap, bif[i].bif_last_cap / 1000, bif[i].bif_last_cap % 1000, f); (void) printf("\tbif_tech = %x\n", bif[i].bif_tech); (void) printf("\tbif_voltage = %4x %2u.%3.3u V\n", bif[i].bif_voltage, bif[i].bif_voltage / 1000, bif[i].bif_voltage % 1000); (void) printf("\tbif_warn_cap = %4x %2u.%3.3u Wh\n", bif[i].bif_warn_cap, bif[i].bif_warn_cap / 1000, bif[i].bif_warn_cap % 1000); (void) printf("\tbif_low_cap = %4x %2u.%3.3u Wh\n", bif[i].bif_low_cap, bif[i].bif_low_cap / 1000, bif[i].bif_low_cap % 1000); (void) printf("\tbif_gran1_cap = %4x %2u.%3.3u Wh\n", bif[i].bif_low_cap, bif[i].bif_low_cap / 1000, bif[i].bif_low_cap % 1000); (void) printf("\tbif_gran2_cap = %4x %2u.%3.3u Wh\n", bif[i].bif_low_cap, bif[i].bif_low_cap / 1000, bif[i].bif_low_cap % 1000); (void) printf("\tbif_model = %s\n", bif[i].bif_model); (void) printf("\tbif_serial = %s\n", bif[i].bif_serial); (void) printf("\tbif_type = %s\n", bif[i].bif_type); (void) printf("\tbif_oem_info = %s\n", bif[i].bif_oem_info); } } if (ioctl(fd, ACPI_BATTERY_STATUS, bst) != 0) { perror("ACPI_BATTERY_STATUS"); exit(1); } if (verbose) { uint32_t j, k; for (i = 0; i < nbat; i++) { (void) printf("battery %d\n", i); (void) printf("\tbst_state = %x\n", bst[i].bst_state); if (bst[i].bst_voltage == 0) { j = k = 0; } else { j = bst[i].bst_rate / bst[i].bst_voltage; k = bst[i].bst_rate * 1000 / bst[i].bst_voltage; } (void) printf("\tbst_rate = %4x %2u.%3.3u W (%u.%3.3u A)\n", bst[i].bst_rate, bst[i].bst_rate / 1000, bst[i].bst_rate % 1000, j, k % 1000); j = bst[i].bst_rem_cap; if (j == -1) j = 0; (void) printf("\tbst_rem_cap = %-8x %2u.%3.3u Wh\n", bst[i].bst_rem_cap, j / 1000, j % 1000); (void) printf("\tbst_voltage = %4x %2u.%3.3u V\n", bst[i].bst_voltage, bst[i].bst_voltage / 1000, bst[i].bst_voltage % 1000); } } for (i = 0; i < nbat; i++) { if (bst[i].bst_rate > 0) { if (bst[i].bst_state & ACPI_BST_CHARGING) minutes = bif[i].bif_last_cap - bst[i].bst_rem_cap; else if (bst[i].bst_state & ACPI_BST_DISCHARGING) minutes = bst[i].bst_rem_cap; else minutes = 0; minutes *= 60; minutes /= bst[i].bst_rate; hours = minutes/60; minutes -= (hours * 60); } else hours = minutes = 0; if ((int)bst[i].bst_rem_cap == -1) { (void) printf("Battery %d not present\n", i); continue; } if (x) { (void) printf("\033]0;%.3g%%\a", bst[i].bst_rem_cap * 100.0/bif[i].bif_last_cap); fflush(stdout); break; } (void) printf("Battery %d status:\n" "\t\tPercentage remaining: %.3g%%\n", i, (bst[i].bst_rem_cap * 100.0/bif[i].bif_last_cap)); if (bst[i].bst_state & (ACPI_BST_CHARGING|ACPI_BST_DISCHARGING)) { (void) printf("\t\tBattery is currently %s\n", bst[i].bst_state & ACPI_BST_CHARGING ? "charging" : "discharging"); } else if (bst[i].bst_rem_cap == bif[i].bif_last_cap) { (void) printf("\t\tBattery is fully charged\n"); } if (bst[i].bst_state & ACPI_BST_CHARGING) (void) printf("\t\tTime remaining to full charge"); else if (bst[i].bst_state & ACPI_BST_DISCHARGING) (void) printf("\t\tTime remaining"); if (bst[i].bst_state & (ACPI_BST_CHARGING|ACPI_BST_DISCHARGING)) { if (bst[i].bst_rate == 0) (void) printf(" unknown\n"); else (void) printf(" %d:%02d\n", hours, minutes); } } if (loop) { if (pollit) { xfd.fd = fd; xfd.events = POLLIN|POLLRDNORM; poll(&xfd, 1, interval * 1000); } else { sleep(interval); } goto loop; } (void) close(fd); return (0); }