apm.c revision 48938
14Srgrimes/*
21690Sdg * apm / zzz	APM BIOS utility for FreeBSD
31690Sdg *
41690Sdg * Copyright (C) 1994-1996 by Tatsumi Hosokawa <hosokawa@jp.FreeBSD.org>
54Srgrimes *
64Srgrimes * This software may be used, modified, copied, distributed, and sold,
74Srgrimes * in both source and binary form provided that the above copyright and
84Srgrimes * these terms are retained. Under no circumstances is the author
94Srgrimes * responsible for the proper functioning of this software, nor does
104Srgrimes * the author assume any responsibility for damages incurred with its
114Srgrimes * use.
124Srgrimes *
134Srgrimes * Sep., 1994	Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD)
144Srgrimes */
154Srgrimes
164Srgrimes#ifndef lint
174Srgrimesstatic const char rcsid[] =
184Srgrimes	"$Id: apm.c,v 1.14 1998/09/04 16:08:54 imp Exp $";
194Srgrimes#endif /* not lint */
204Srgrimes
214Srgrimes#include <sys/file.h>
224Srgrimes#include <sys/ioctl.h>
234Srgrimes#include <sys/types.h>
244Srgrimes#include <sys/sysctl.h>
254Srgrimes
264Srgrimes#include <machine/apm_bios.h>
274Srgrimes
284Srgrimes#include <err.h>
294Srgrimes#include <stdio.h>
304Srgrimes#include <stdlib.h>
314Srgrimes#include <string.h>
324Srgrimes#include <sys/file.h>
334Srgrimes#include <sys/ioctl.h>
344Srgrimes#include <time.h>
354Srgrimes#include <unistd.h>
364Srgrimes
37608Srgrimes#define APMDEV	"/dev/apm"
3850477Speter
394Srgrimes#define xh(a)	(((a) & 0xff00) >> 8)
404Srgrimes#define xl(a)	((a) & 0xff)
414Srgrimes#define APMERR(a) xh(a)
421704Sdg
434Srgrimesint cmos_wall = 0;	/* True when wall time is in cmos clock, else UTC */
444Srgrimes
4571257Spetervoid
4631544Sjmgusage()
4731544Sjmg{
4813203Swollman	fprintf(stderr, "%s\n%s\n",
4971257Speter		"usage: apm [-ablstzZ] [-d 1|0] [-r delta]",
5032925Seivind		"       zzz");
5113203Swollman	exit(1);
521549Srgrimes}
5365557Sjasone
541549Srgrimesint
551549Srgrimesint2bcd(int i)
5631564Ssef{
5767365Sjhb	int retval = 0;
581549Srgrimes	int base = 0;
5965557Sjasone
6067365Sjhb	if (i >= 10000)
6131389Sbde		return -1;
6231389Sbde
631549Srgrimes	while (i) {
6464294Sps		retval |= (i % 10) << base;
652257Ssos		i /= 10;
6634924Sbde		base += 4;
6712662Sdg	}
684Srgrimes	return retval;
691549Srgrimes}
704Srgrimes
714Srgrimesint
7212662Sdgbcd2int(int bcd)
731549Srgrimes{
7422521Sdyson	int retval = 0;
751549Srgrimes
767090Sbde	if (bcd > 0x9999)
771549Srgrimes		return -1;
781549Srgrimes
7912662Sdg	while (bcd) {
804Srgrimes		retval = retval * 10 + ((bcd & 0xf000) >> 12);
811549Srgrimes		bcd = (bcd & 0xfff) << 4;
827090Sbde	}
8331389Sbde	return retval;
8431389Sbde}
8525164Speter
8631389Sbdevoid
8730275Speterapm_suspend(int fd)
881549Srgrimes{
8965557Sjasone	if (ioctl(fd, APMIO_SUSPEND, NULL) == -1)
9031389Sbde		err(1, NULL);
9131389Sbde}
929545Sjoerg
9318207Sbdevoid
9418207Sbdeapm_standby(int fd)
959545Sjoerg{
969545Sjoerg	if (ioctl(fd, APMIO_STANDBY, NULL) == -1)
9730275Speter		err(1, NULL);
9830275Speter}
9955823Syokota
10042135Smsmithvoid
1011549Srgrimesapm_getinfo(int fd, apm_info_t aip)
1021549Srgrimes{
10365557Sjasone	if (ioctl(fd, APMIO_GETINFO, aip) == -1)
10465557Sjasone		err(1, NULL);
10512817Sphk}
10612817Sphk
10711343Sbdevoid
10811343Sbdeprint_all_info(int fd, apm_info_t aip)
10958717Sdillon{
11065557Sjasone	struct apm_bios_arg args;
11111343Sbde	int apmerr;
11241454Skato
11341454Skato	printf("APM version: %d.%d\n", aip->ai_major, aip->ai_minor);
11412929Sdg	printf("APM Managment: %s\n", (aip->ai_status ? "Enabled" : "Disabled"));
1154Srgrimes	printf("AC Line status: ");
11611163Sjulian	if (aip->ai_acline == 255)
11711163Sjulian		printf("unknown");
11817521Sdg	else if (aip->ai_acline > 1)
11912702Sphk		printf("invalid value (0x%x)", aip->ai_acline);
1205603Sbde	else {
121757Sdg		char messages[][10] = {"off-line", "on-line"};
1225603Sbde		printf("%s", messages[aip->ai_acline]);
123757Sdg	}
124757Sdg	printf("\n");
1255603Sbde	printf("Battery status: ");
126757Sdg	if (aip->ai_batt_stat == 255)
127757Sdg		printf("unknown");
1285603Sbde	else if (aip->ai_batt_stat > 3)
1291690Sdg			printf("invalid value (0x%x)", aip->ai_batt_stat);
130757Sdg	else {
131757Sdg		char messages[][10] = {"high", "low", "critical", "charging"};
132757Sdg		printf("%s", messages[aip->ai_batt_stat]);
1335603Sbde	}
134757Sdg	printf("\n");
1355603Sbde	printf("Remaining battery life: ");
1365603Sbde	if (aip->ai_batt_life == 255)
1375603Sbde		printf("unknown");
138757Sdg	else if (aip->ai_batt_life <= 100)
139757Sdg		printf("%d%%", aip->ai_batt_life);
140757Sdg	else
141757Sdg		printf("invalid value (0x%x)", aip->ai_batt_life);
142757Sdg	printf("\n");
143757Sdg	printf("Remaining battery time: ");
144757Sdg	if (aip->ai_batt_time == -1)
145757Sdg		printf("unknown");
146757Sdg	else {
147757Sdg		int t, h, m, s;
14817521Sdg
149757Sdg		t = aip->ai_batt_time;
1504Srgrimes		s = t % 60;
15131535Sjkh		t /= 60;
15231507Ssef		m = t % 60;
15331507Ssef		t /= 60;
15431507Ssef		h = t;
15564294Sps		printf("%2d:%02d:%02d", h, m, s);
15664294Sps	}
15764294Sps	printf("\n");
15864294Sps	if (aip->ai_infoversion >= 1) {
15964294Sps		printf("Number of batteries: ");
16064294Sps		if (aip->ai_batteries == (u_int) -1)
16164294Sps			printf("unknown\n");
16264294Sps		else
16364294Sps			printf("%d\n", aip->ai_batteries);
16469881Sjake	}
16569881Sjake
16669881Sjake	/*
16769881Sjake	 * try to get the suspend timer
16871527Sjhb	 */
16971527Sjhb	bzero(&args, sizeof(args));
1701690Sdg	args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER;
1711690Sdg	args.ebx = PMDV_APMBIOS;
1721690Sdg	args.ecx = 0x0001;
1731690Sdg	if (ioctl(fd, APMIO_BIOS, &args)) {
17471527Sjhb		err(1,"Get resume timer");
175757Sdg	} else {
17658717Sdillon		apmerr = APMERR(args.eax);
17771527Sjhb		 if (apmerr == 0x0d || apmerr == 0x86)
17865557Sjasone			printf("Resume timer: disabled\n");
1791690Sdg		else if (apmerr)
18058717Sdillon			fprintf(stderr,
18128013Sdyson			    "Failed to get the resume timer: APM error0x%x\n",
18271527Sjhb			    apmerr);
1831690Sdg		else {
18458717Sdillon			/*
1851690Sdg			 * OK.  We have the time (all bcd).
1861690Sdg			 * CH - seconds
1871690Sdg			 * DH - hours
1881690Sdg			 * DL - minutes
1891690Sdg			 * xh(SI) - month (1-12)
1901690Sdg			 * xl(SI) - day of month (1-31)
1911690Sdg			 * DI - year
1921690Sdg			 */
19368808Sjhb			struct tm tm;
1941690Sdg			char buf[1024];
1951690Sdg			time_t t;
1961690Sdg
19765557Sjasone			tm.tm_sec = bcd2int(xh(args.ecx));
19868808Sjhb			tm.tm_min = bcd2int(xl(args.edx));
19965557Sjasone			tm.tm_hour = bcd2int(xh(args.edx));
20071527Sjhb			tm.tm_mday = bcd2int(xl(args.esi));
20165557Sjasone			tm.tm_mon = bcd2int(xh(args.esi)) - 1;
2021690Sdg			tm.tm_year = bcd2int(args.edi) - 1900;
20365557Sjasone			if (cmos_wall)
20471527Sjhb				t = mktime(&tm);
2051690Sdg			else
20671527Sjhb				t = timegm(&tm);
2076296Sdg			tm = *localtime(&t);
2086296Sdg			strftime(buf, sizeof(buf), "%c", &tm);
2096296Sdg			printf("Resume timer: %s\n", buf);
21071527Sjhb		}
21171527Sjhb	}
21271527Sjhb
21371527Sjhb	/*
21465557Sjasone	 * Get the ring indicator resume state
21571527Sjhb	 */
21616725Sbde	bzero(&args, sizeof(args));
21716725Sbde	args.eax  = (APM_BIOS) << 8 | APM_RESUMEONRING;
21858717Sdillon	args.ebx = PMDV_APMBIOS;
2191690Sdg	args.ecx = 0x0002;
22071527Sjhb	if (ioctl(fd, APMIO_BIOS, &args) == 0) {
2211690Sdg		printf("Resume on ring indicator: %sabled\n",
2221690Sdg			args.ecx ? "en" : "dis");
2234Srgrimes	}
22411343Sbde
2251690Sdg	if (aip->ai_infoversion >= 1) {
2264Srgrimes		printf("APM Capacities:\n", aip->ai_capabilities);
2271690Sdg		if (aip->ai_capabilities == 0xff00)
2284Srgrimes			printf("\tunknown\n");
2294Srgrimes		if (aip->ai_capabilities & 0x01)
230798Swollman			printf("\tglobal standby state\n");
2314Srgrimes		if (aip->ai_capabilities & 0x02)
2324Srgrimes			printf("\tglobal suspend state\n");
2334Srgrimes		if (aip->ai_capabilities & 0x04)
2341690Sdg			printf("\tresume timer from standby\n");
2351549Srgrimes		if (aip->ai_capabilities & 0x08)
2363436Sphk			printf("\tresume timer from suspend\n");
23741454Skato		if (aip->ai_capabilities & 0x10)
23865557Sjasone			printf("\tRI resume from standby\n");
23965557Sjasone		if (aip->ai_capabilities & 0x20)
24065557Sjasone			printf("\tRI resume from suspend\n");
2414Srgrimes		if (aip->ai_capabilities & 0x40)
24265557Sjasone			printf("\tPCMCIA RI resume from standby\n");
24365557Sjasone		if (aip->ai_capabilities & 0x80)
24465557Sjasone			printf("\tPCMCIA RI resume from suspend\n");
24541454Skato	}
24665557Sjasone
24765557Sjasone}
24865557Sjasone
24965557Sjasone/*
25071527Sjhb * currently, it can turn off the display, but the display never comes
25171527Sjhb * back until the machine suspend/resumes :-).
25241454Skato */
25341454Skatovoid
25441454Skatoapm_display(int fd, int newstate)
25541454Skato{
25641454Skato	if (ioctl(fd, APMIO_DISPLAY, &newstate) == -1)
25741454Skato		err(1, NULL);
25841454Skato}
25941454Skato
26041454Skato
26141454Skatovoid
26241454Skatoapm_set_timer(int fd, int delta)
26341454Skato{
26441454Skato	time_t tmr;
26571527Sjhb	struct tm *tm;
26671527Sjhb	struct apm_bios_arg args;
26771527Sjhb
26871527Sjhb	tmr = time(NULL) + delta;
26941454Skato	if (cmos_wall)
27041454Skato		tm = localtime(&tmr);
27141454Skato	else
27241454Skato		tm = gmtime(&tmr);
27341454Skato	bzero(&args, sizeof(args));
27441454Skato	args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER;
27565557Sjasone	args.ebx = PMDV_APMBIOS;
27665557Sjasone	if (delta > 0) {
27765557Sjasone		args.ecx = (int2bcd(tm->tm_sec) << 8) | 0x02;
27865557Sjasone		args.edx = (int2bcd(tm->tm_hour) << 8) | int2bcd(tm->tm_min);
27965557Sjasone		args.esi = (int2bcd(tm->tm_mon + 1) << 8) | int2bcd(tm->tm_mday);
28041454Skato		args.edi = int2bcd(tm->tm_year + 1900);
28141454Skato	} else {
28241454Skato		args.ecx = 0x0000;
28365557Sjasone	}
28441454Skato	if (ioctl(fd, APMIO_BIOS, &args)) {
28565811Sbde		err(1,"Set resume timer");
28665557Sjasone	}
28731535Sjkh}
28831507Ssef
28931507Ssefint
29065557Sjasonemain(int argc, char *argv[])
2914Srgrimes{
2921690Sdg	int	c, fd;
2938876Srgrimes	int     sleep = 0, all_info = 1, apm_status = 0, batt_status = 0;
29465557Sjasone	int     display = 0, batt_life = 0, ac_status = 0, standby = 0;
29565557Sjasone	int	batt_time = 0, delta = 0;
2961690Sdg	char	*cmdname;
297200Sdg	size_t	cmos_wall_len = sizeof(cmos_wall);
29871527Sjhb
2991690Sdg	if (sysctlbyname("machdep.wall_cmos_clock", &cmos_wall, &cmos_wall_len,
30071527Sjhb	    NULL, 0) == -1)
30125555Speter		err(1, "sysctlbyname(machdep.wall_cmos_clock)");
3024Srgrimes	if ((cmdname = strrchr(argv[0], '/')) != NULL)
3031690Sdg		cmdname++;
3041690Sdg	else
3051690Sdg		cmdname = argv[0];
3061690Sdg
3071690Sdg	if (strcmp(cmdname, "zzz") == 0) {
308974Sdg		sleep = 1;
3091690Sdg		all_info = 0;
3101690Sdg		goto finish_option;
3111690Sdg	}
3121690Sdg	while ((c = getopt(argc, argv, "ablRr:stzd:Z")) != -1) {
3131690Sdg		switch (c) {
314974Sdg		case 'a':
3151690Sdg			ac_status = 1;
3161690Sdg			all_info = 0;
3171690Sdg			break;
3181690Sdg		case 'b':
3194Srgrimes			batt_status = 1;
32027993Sdyson			all_info = 0;
32127993Sdyson			break;
32227993Sdyson		case 'd':
32327993Sdyson			display = *optarg - '0';
32427993Sdyson			if (display < 0 || display > 1) {
3251690Sdg				warnx("argument of option '-%c' is invalid", c);
32627993Sdyson				usage();
32728872Sjlemon			}
32828872Sjlemon			display++;
32927993Sdyson			all_info = 0;
33065557Sjasone			break;
33127993Sdyson		case 'l':
33227993Sdyson			batt_life = 1;
33327993Sdyson			all_info = 0;
33427993Sdyson			break;
3351690Sdg		case 'R':
3365603Sbde			delta = -1;
3375603Sbde			break;
3385603Sbde		case 'r':
3391690Sdg			delta = atoi(optarg);
3401690Sdg			break;
3411690Sdg		case 's':
3424Srgrimes			apm_status = 1;
3431690Sdg			all_info = 0;
34441454Skato			break;
34531535Sjkh		case 't':
34665557Sjasone			batt_time = 1;
34765557Sjasone			all_info = 0;
34865557Sjasone			break;
34965557Sjasone		case 'z':
35065557Sjasone			sleep = 1;
35165557Sjasone			all_info = 0;
35231507Ssef			break;
35365557Sjasone		case 'Z':
35431507Ssef			standby = 1;
35565557Sjasone			all_info = 0;
35665557Sjasone			break;
3571690Sdg		case '?':
35865557Sjasone		default:
3594Srgrimes			usage();
3601690Sdg		}
3611690Sdg		argc -= optind;
3624Srgrimes		argv += optind;
3631690Sdg	}
36449081Scracauerfinish_option:
3651690Sdg	fd = open(APMDEV, O_RDWR);
3661690Sdg	if (fd == -1) {
3674Srgrimes		warn("can't open %s", APMDEV);
3681690Sdg		return 1;
3691690Sdg	}
3709545Sjoerg	if (delta)
37165557Sjasone		apm_set_timer(fd, delta);
37265557Sjasone	if (sleep)
37365557Sjasone		apm_suspend(fd);
37465557Sjasone	else if (standby)
37565557Sjasone		apm_standby(fd);
37665557Sjasone	else if (delta == 0) {
37765557Sjasone		struct apm_info info;
37865557Sjasone
37965557Sjasone		apm_getinfo(fd, &info);
3809545Sjoerg		if (all_info)
38163140Sps			print_all_info(fd, &info);
38263140Sps		if (ac_status)
3832320Sdg			printf("%d\n", info.ai_acline);
38464294Sps		if (batt_status)
38564294Sps			printf("%d\n", info.ai_batt_stat);
38664294Sps		if (batt_life)
38764294Sps			printf("%d\n", info.ai_batt_life);
38864294Sps		if (apm_status)
38964294Sps			printf("%d\n", info.ai_status);
39064294Sps		if (batt_time)
39164294Sps			printf("%d\n", info.ai_batt_time);
39263140Sps		if (display)
39365557Sjasone			apm_display(fd, display - 1);
39464294Sps	}
39564294Sps	close(fd);
39664294Sps	return 0;
3979545Sjoerg}
3989545Sjoerg