505 lines
11 KiB
C
505 lines
11 KiB
C
/*
|
|
* apm / zzz APM BIOS utility for FreeBSD
|
|
*
|
|
* Copyright (C) 1994-1996 by Tatsumi Hosokawa <hosokawa@jp.FreeBSD.org>
|
|
*
|
|
* This software may be used, modified, copied, distributed, and sold,
|
|
* in both source and binary form provided that the above copyright and
|
|
* these terms are retained. Under no circumstances is the author
|
|
* responsible for the proper functioning of this software, nor does
|
|
* the author assume any responsibility for damages incurred with its
|
|
* use.
|
|
*
|
|
* Sep., 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD)
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include <sys/file.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/sysctl.h>
|
|
|
|
#include <machine/apm_bios.h>
|
|
|
|
#include <err.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/file.h>
|
|
#include <sys/ioctl.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#define APMDEV "/dev/apm"
|
|
|
|
#define xh(a) (((a) & 0xff00) >> 8)
|
|
#define xl(a) ((a) & 0xff)
|
|
#define APMERR(a) xh(a)
|
|
|
|
int cmos_wall = 0; /* True when wall time is in cmos clock, else UTC */
|
|
|
|
static void
|
|
usage(void)
|
|
{
|
|
fprintf(stderr, "%s\n%s\n",
|
|
"usage: apm [-ablstzZ] [-d enable ] [ -e enable ] "
|
|
"[ -h enable ] [-r delta]",
|
|
" zzz");
|
|
exit(1);
|
|
}
|
|
|
|
/*
|
|
* Return 1 for boolean true, and 0 for false, according to the
|
|
* interpretation of the string argument given.
|
|
*/
|
|
static int
|
|
is_true(const char *boolean)
|
|
{
|
|
char *endp;
|
|
long val;
|
|
|
|
val = strtoul(boolean, &endp, 0);
|
|
if (*endp == '\0')
|
|
return (val != 0 ? 1 : 0);
|
|
if (strcasecmp(boolean, "true") == 0 ||
|
|
strcasecmp(boolean, "yes") == 0 ||
|
|
strcasecmp(boolean, "enable") == 0)
|
|
return (1);
|
|
if (strcasecmp(boolean, "false") == 0 ||
|
|
strcasecmp(boolean, "no") == 0 ||
|
|
strcasecmp(boolean, "disable") == 0)
|
|
return (0);
|
|
/* Well, I have no idea what the user wants, so... */
|
|
warnx("invalid boolean argument \"%s\"", boolean);
|
|
usage();
|
|
/* NOTREACHED */
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
int2bcd(int i)
|
|
{
|
|
int retval = 0;
|
|
int base = 0;
|
|
|
|
if (i >= 10000)
|
|
return -1;
|
|
|
|
while (i) {
|
|
retval |= (i % 10) << base;
|
|
i /= 10;
|
|
base += 4;
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
static int
|
|
bcd2int(int bcd)
|
|
{
|
|
int retval = 0;
|
|
int place = 1;
|
|
|
|
if (bcd > 0x9999)
|
|
return -1;
|
|
|
|
while (bcd) {
|
|
retval += (bcd & 0xf) * place;
|
|
bcd >>= 4;
|
|
place *= 10;
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
static void
|
|
apm_suspend(int fd)
|
|
{
|
|
if (ioctl(fd, APMIO_SUSPEND, NULL) == -1)
|
|
err(1, "ioctl(APMIO_SUSPEND)");
|
|
}
|
|
|
|
static void
|
|
apm_standby(int fd)
|
|
{
|
|
if (ioctl(fd, APMIO_STANDBY, NULL) == -1)
|
|
err(1, "ioctl(APMIO_STANDBY)");
|
|
}
|
|
|
|
static void
|
|
apm_getinfo(int fd, apm_info_t aip)
|
|
{
|
|
if (ioctl(fd, APMIO_GETINFO, aip) == -1)
|
|
err(1, "ioctl(APMIO_GETINFO)");
|
|
}
|
|
|
|
static void
|
|
apm_enable(int fd, int enable)
|
|
{
|
|
if (enable) {
|
|
if (ioctl(fd, APMIO_ENABLE) == -1)
|
|
err(1, "ioctl(APMIO_ENABLE)");
|
|
} else {
|
|
if (ioctl(fd, APMIO_DISABLE) == -1)
|
|
err(1, "ioctl(APMIO_DISABLE)");
|
|
}
|
|
}
|
|
|
|
static void
|
|
print_batt_time(int batt_time)
|
|
{
|
|
printf("Remaining battery time: ");
|
|
if (batt_time == -1)
|
|
printf("unknown\n");
|
|
else {
|
|
int h, m, s;
|
|
|
|
h = batt_time;
|
|
s = h % 60;
|
|
h /= 60;
|
|
m = h % 60;
|
|
h /= 60;
|
|
printf("%2d:%02d:%02d\n", h, m, s);
|
|
}
|
|
}
|
|
|
|
static void
|
|
print_batt_life(u_int batt_life)
|
|
{
|
|
printf("Remaining battery life: ");
|
|
if (batt_life >= 255)
|
|
printf("unknown\n");
|
|
else if (batt_life <= 100)
|
|
printf("%d%%\n", batt_life);
|
|
else
|
|
printf("invalid value (0x%x)\n", batt_life);
|
|
}
|
|
|
|
static void
|
|
print_batt_stat(u_int batt_stat)
|
|
{
|
|
const char *batt_msg[] = { "high", "low", "critical", "charging" };
|
|
|
|
printf("Battery Status: ");
|
|
if (batt_stat >= 255)
|
|
printf("unknown\n");
|
|
else if (batt_stat > 3)
|
|
printf("invalid value (0x%x)\n", batt_stat);
|
|
else
|
|
printf("%s\n", batt_msg[batt_stat]);
|
|
}
|
|
|
|
static void
|
|
print_all_info(int fd, apm_info_t aip, int bioscall_available)
|
|
{
|
|
struct apm_bios_arg args;
|
|
int apmerr;
|
|
const char *line_msg[] = { "off-line", "on-line" };
|
|
|
|
printf("APM version: %d.%d\n", aip->ai_major, aip->ai_minor);
|
|
printf("APM Management: %s\n", aip->ai_status ? "Enabled" : "Disabled");
|
|
printf("AC Line status: ");
|
|
if (aip->ai_acline >= 255)
|
|
printf("unknown\n");
|
|
else if (aip->ai_acline > 1)
|
|
printf("invalid value (0x%x)\n", aip->ai_acline);
|
|
else
|
|
printf("%s\n", line_msg[aip->ai_acline]);
|
|
|
|
print_batt_stat(aip->ai_batt_stat);
|
|
print_batt_life(aip->ai_batt_life);
|
|
print_batt_time(aip->ai_batt_time);
|
|
|
|
if (aip->ai_infoversion >= 1) {
|
|
printf("Number of batteries: ");
|
|
if (aip->ai_batteries >= 255)
|
|
printf("unknown\n");
|
|
else {
|
|
u_int i;
|
|
struct apm_pwstatus aps;
|
|
|
|
printf("%d\n", aip->ai_batteries);
|
|
for (i = 0; i < aip->ai_batteries; ++i) {
|
|
bzero(&aps, sizeof(aps));
|
|
aps.ap_device = PMDV_BATT0 + i;
|
|
if (ioctl(fd, APMIO_GETPWSTATUS, &aps) == -1)
|
|
continue;
|
|
printf("Battery %d:\n", i);
|
|
if (aps.ap_batt_flag <= 255 &&
|
|
(aps.ap_batt_flag & APM_BATT_NOT_PRESENT)) {
|
|
printf("not present\n");
|
|
continue;
|
|
}
|
|
|
|
printf("\t");
|
|
print_batt_stat(aps.ap_batt_stat);
|
|
printf("\t");
|
|
print_batt_life(aps.ap_batt_life);
|
|
printf("\t");
|
|
print_batt_time(aps.ap_batt_time);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bioscall_available) {
|
|
/*
|
|
* try to get the suspend timer
|
|
*/
|
|
bzero(&args, sizeof(args));
|
|
args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER;
|
|
args.ebx = PMDV_APMBIOS;
|
|
args.ecx = 0x0001;
|
|
if (ioctl(fd, APMIO_BIOS, &args)) {
|
|
printf("Resume timer: unknown\n");
|
|
} else {
|
|
apmerr = APMERR(args.eax);
|
|
if (apmerr == 0x0d || apmerr == 0x86)
|
|
printf("Resume timer: disabled\n");
|
|
else if (apmerr)
|
|
warnx(
|
|
"failed to get the resume timer: APM error0x%x", apmerr);
|
|
else {
|
|
/*
|
|
* OK. We have the time (all bcd).
|
|
* CH - seconds
|
|
* DH - hours
|
|
* DL - minutes
|
|
* xh(SI) - month (1-12)
|
|
* xl(SI) - day of month (1-31)
|
|
* DI - year
|
|
*/
|
|
struct tm tm;
|
|
char buf[1024];
|
|
time_t t;
|
|
|
|
tm.tm_sec = bcd2int(xh(args.ecx));
|
|
tm.tm_min = bcd2int(xl(args.edx));
|
|
tm.tm_hour = bcd2int(xh(args.edx));
|
|
tm.tm_mday = bcd2int(xl(args.esi));
|
|
tm.tm_mon = bcd2int(xh(args.esi)) - 1;
|
|
tm.tm_year = bcd2int(args.edi) - 1900;
|
|
if (cmos_wall)
|
|
t = mktime(&tm);
|
|
else
|
|
t = timegm(&tm);
|
|
if (t != -1) {
|
|
tm = *localtime(&t);
|
|
strftime(buf, sizeof(buf), "%c", &tm);
|
|
printf("Resume timer: %s\n", buf);
|
|
} else
|
|
printf("Resume timer: unknown\n");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Get the ring indicator resume state
|
|
*/
|
|
bzero(&args, sizeof(args));
|
|
args.eax = (APM_BIOS) << 8 | APM_RESUMEONRING;
|
|
args.ebx = PMDV_APMBIOS;
|
|
args.ecx = 0x0002;
|
|
if (ioctl(fd, APMIO_BIOS, &args) == 0) {
|
|
printf("Resume on ring indicator: %sabled\n",
|
|
args.ecx ? "en" : "dis");
|
|
}
|
|
}
|
|
|
|
if (aip->ai_infoversion >= 1) {
|
|
printf("APM Capabilities:\n");
|
|
if (aip->ai_capabilities == 0xff00)
|
|
printf("\tunknown\n");
|
|
if (aip->ai_capabilities & 0x01)
|
|
printf("\tglobal standby state\n");
|
|
if (aip->ai_capabilities & 0x02)
|
|
printf("\tglobal suspend state\n");
|
|
if (aip->ai_capabilities & 0x04)
|
|
printf("\tresume timer from standby\n");
|
|
if (aip->ai_capabilities & 0x08)
|
|
printf("\tresume timer from suspend\n");
|
|
if (aip->ai_capabilities & 0x10)
|
|
printf("\tRI resume from standby\n");
|
|
if (aip->ai_capabilities & 0x20)
|
|
printf("\tRI resume from suspend\n");
|
|
if (aip->ai_capabilities & 0x40)
|
|
printf("\tPCMCIA RI resume from standby\n");
|
|
if (aip->ai_capabilities & 0x80)
|
|
printf("\tPCMCIA RI resume from suspend\n");
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* currently, it can turn off the display, but the display never comes
|
|
* back until the machine suspend/resumes :-).
|
|
*/
|
|
static void
|
|
apm_display(int fd, int newstate)
|
|
{
|
|
if (ioctl(fd, APMIO_DISPLAY, &newstate) == -1)
|
|
err(1, "ioctl(APMIO_DISPLAY)");
|
|
}
|
|
|
|
static void
|
|
apm_haltcpu(int fd, int enable)
|
|
{
|
|
if (enable) {
|
|
if (ioctl(fd, APMIO_HALTCPU, NULL) == -1)
|
|
err(1, "ioctl(APMIO_HALTCPU)");
|
|
} else {
|
|
if (ioctl(fd, APMIO_NOTHALTCPU, NULL) == -1)
|
|
err(1, "ioctl(APMIO_NOTHALTCPU)");
|
|
}
|
|
}
|
|
|
|
static void
|
|
apm_set_timer(int fd, int delta)
|
|
{
|
|
time_t tmr;
|
|
struct tm *tm;
|
|
struct apm_bios_arg args;
|
|
|
|
tmr = time(NULL) + delta;
|
|
if (cmos_wall)
|
|
tm = localtime(&tmr);
|
|
else
|
|
tm = gmtime(&tmr);
|
|
bzero(&args, sizeof(args));
|
|
args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER;
|
|
args.ebx = PMDV_APMBIOS;
|
|
if (delta > 0) {
|
|
args.ecx = (int2bcd(tm->tm_sec) << 8) | 0x02;
|
|
args.edx = (int2bcd(tm->tm_hour) << 8) | int2bcd(tm->tm_min);
|
|
args.esi = (int2bcd(tm->tm_mon + 1) << 8) | int2bcd(tm->tm_mday);
|
|
args.edi = int2bcd(tm->tm_year + 1900);
|
|
} else {
|
|
args.ecx = 0x0000;
|
|
}
|
|
if (ioctl(fd, APMIO_BIOS, &args)) {
|
|
err(1,"set resume timer");
|
|
}
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
int c, fd;
|
|
int dosleep = 0, all_info = 1, apm_status = 0, batt_status = 0;
|
|
int display = -1, batt_life = 0, ac_status = 0, standby = 0;
|
|
int batt_time = 0, delta = 0, enable = -1, haltcpu = -1;
|
|
char *cmdname;
|
|
int bioscall_available = 0;
|
|
size_t cmos_wall_len = sizeof(cmos_wall);
|
|
|
|
if (sysctlbyname("machdep.wall_cmos_clock", &cmos_wall, &cmos_wall_len,
|
|
NULL, 0) == -1)
|
|
err(1, "sysctlbyname(machdep.wall_cmos_clock)");
|
|
if ((cmdname = strrchr(argv[0], '/')) != NULL)
|
|
cmdname++;
|
|
else
|
|
cmdname = argv[0];
|
|
|
|
if (strcmp(cmdname, "zzz") == 0) {
|
|
dosleep = 1;
|
|
all_info = 0;
|
|
goto finish_option;
|
|
}
|
|
while ((c = getopt(argc, argv, "abe:h:lRr:stzd:Z")) != -1) {
|
|
switch (c) {
|
|
case 'a':
|
|
ac_status = 1;
|
|
all_info = 0;
|
|
break;
|
|
case 'b':
|
|
batt_status = 1;
|
|
all_info = 0;
|
|
break;
|
|
case 'd':
|
|
display = is_true(optarg);
|
|
all_info = 0;
|
|
break;
|
|
case 'l':
|
|
batt_life = 1;
|
|
all_info = 0;
|
|
break;
|
|
case 'R':
|
|
delta = -1;
|
|
break;
|
|
case 'r':
|
|
delta = atoi(optarg);
|
|
break;
|
|
case 's':
|
|
apm_status = 1;
|
|
all_info = 0;
|
|
break;
|
|
case 'e':
|
|
enable = is_true(optarg);
|
|
all_info = 0;
|
|
break;
|
|
case 'h':
|
|
haltcpu = is_true(optarg);
|
|
all_info = 0;
|
|
break;
|
|
case 't':
|
|
batt_time = 1;
|
|
all_info = 0;
|
|
break;
|
|
case 'z':
|
|
dosleep = 1;
|
|
all_info = 0;
|
|
break;
|
|
case 'Z':
|
|
standby = 1;
|
|
all_info = 0;
|
|
break;
|
|
case '?':
|
|
default:
|
|
usage();
|
|
}
|
|
argc -= optind;
|
|
argv += optind;
|
|
}
|
|
finish_option:
|
|
if (haltcpu != -1 || enable != -1 || display != -1 || delta || dosleep
|
|
|| standby) {
|
|
fd = open(APMDEV, O_RDWR);
|
|
bioscall_available = 1;
|
|
} else if ((fd = open(APMDEV, O_RDWR)) >= 0)
|
|
bioscall_available = 1;
|
|
else
|
|
fd = open(APMDEV, O_RDONLY);
|
|
if (fd == -1)
|
|
err(1, "can't open %s", APMDEV);
|
|
if (enable != -1)
|
|
apm_enable(fd, enable);
|
|
if (haltcpu != -1)
|
|
apm_haltcpu(fd, haltcpu);
|
|
if (delta)
|
|
apm_set_timer(fd, delta);
|
|
if (dosleep)
|
|
apm_suspend(fd);
|
|
else if (standby)
|
|
apm_standby(fd);
|
|
else if (delta == 0) {
|
|
struct apm_info info;
|
|
|
|
apm_getinfo(fd, &info);
|
|
if (all_info)
|
|
print_all_info(fd, &info, bioscall_available);
|
|
if (ac_status)
|
|
printf("%d\n", info.ai_acline);
|
|
if (batt_status)
|
|
printf("%d\n", info.ai_batt_stat);
|
|
if (batt_life)
|
|
printf("%d\n", info.ai_batt_life);
|
|
if (apm_status)
|
|
printf("%d\n", info.ai_status);
|
|
if (batt_time)
|
|
printf("%d\n", info.ai_batt_time);
|
|
if (display != -1)
|
|
apm_display(fd, display);
|
|
}
|
|
close(fd);
|
|
exit(0);
|
|
}
|