xref: /freebsd/usr.sbin/apm/apm.c (revision a8445737e740901f5f2c8d24c12ef7fc8b00134e)
1 /*
2  * apm / zzz	APM BIOS utility for FreeBSD
3  *
4  * Copyright (C) 1994-1996 by Tatsumi Hosokawa <hosokawa@jp.FreeBSD.org>
5  *
6  * This software may be used, modified, copied, distributed, and sold,
7  * in both source and binary form provided that the above copyright and
8  * these terms are retained. Under no circumstances is the author
9  * responsible for the proper functioning of this software, nor does
10  * the author assume any responsibility for damages incurred with its
11  * use.
12  *
13  * Sep., 1994	Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD)
14  */
15 
16 #ifndef lint
17 static const char rcsid[] =
18 	"$Id: apm.c,v 1.13 1998/02/20 07:17:46 hosokawa Exp $";
19 #endif /* not lint */
20 
21 #include <err.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/file.h>
26 #include <sys/ioctl.h>
27 #include <unistd.h>
28 #include <machine/apm_bios.h>
29 #include <time.h>
30 
31 #define APMDEV	"/dev/apm"
32 
33 #define xh(a)	(((a) & 0xff00) >> 8)
34 #define xl(a)	((a) & 0xff)
35 #define APMERR(a) xh(a)
36 
37 int cmos_wall = 0;	/* True when wall time is in cmos clock, else UTC */
38 
39 void
40 usage()
41 {
42 	fprintf(stderr, "%s\n%s\n",
43 		"usage: apm [-ablstzZ] [-d 1|0] [-r delta]",
44 		"       zzz");
45 	exit(1);
46 }
47 
48 int
49 int2bcd(int i)
50 {
51 	int retval = 0;
52 	int base = 0;
53 
54 	if (i >= 10000)
55 		return -1;
56 
57 	while (i) {
58 		retval |= (i % 10) << base;
59 		i /= 10;
60 		base += 4;
61 	}
62 	return retval;
63 }
64 
65 int
66 bcd2int(int bcd)
67 {
68 	int retval = 0;
69 
70 	if (bcd > 0x9999)
71 		return -1;
72 
73 	while (bcd) {
74 		retval = retval * 10 + ((bcd & 0xf000) >> 12);
75 		bcd = (bcd & 0xfff) << 4;
76 	}
77 	return retval;
78 }
79 
80 void
81 apm_suspend(int fd)
82 {
83 	if (ioctl(fd, APMIO_SUSPEND, NULL) == -1)
84 		err(1, NULL);
85 }
86 
87 void
88 apm_standby(int fd)
89 {
90 	if (ioctl(fd, APMIO_STANDBY, NULL) == -1)
91 		err(1, NULL);
92 }
93 
94 void
95 apm_getinfo(int fd, apm_info_t aip)
96 {
97 	if (ioctl(fd, APMIO_GETINFO, aip) == -1)
98 		err(1, NULL);
99 }
100 
101 void
102 print_all_info(int fd, apm_info_t aip)
103 {
104 	struct apm_bios_arg args;
105 	int apmerr;
106 
107 	printf("APM version: %d.%d\n", aip->ai_major, aip->ai_minor);
108 	printf("APM Managment: %s\n", (aip->ai_status ? "Enabled" : "Disabled"));
109 	printf("AC Line status: ");
110 	if (aip->ai_acline == 255)
111 		printf("unknown");
112 	else if (aip->ai_acline > 1)
113 		printf("invalid value (0x%x)", aip->ai_acline);
114 	else {
115 		char messages[][10] = {"off-line", "on-line"};
116 		printf("%s", messages[aip->ai_acline]);
117 	}
118 	printf("\n");
119 	printf("Battery status: ");
120 	if (aip->ai_batt_stat == 255)
121 		printf("unknown");
122 	else if (aip->ai_batt_stat > 3)
123 			printf("invalid value (0x%x)", aip->ai_batt_stat);
124 	else {
125 		char messages[][10] = {"high", "low", "critical", "charging"};
126 		printf("%s", messages[aip->ai_batt_stat]);
127 	}
128 	printf("\n");
129 	printf("Remaining battery life: ");
130 	if (aip->ai_batt_life == 255)
131 		printf("unknown");
132 	else if (aip->ai_batt_life <= 100)
133 		printf("%d%%", aip->ai_batt_life);
134 	else
135 		printf("invalid value (0x%x)", aip->ai_batt_life);
136 	printf("\n");
137 	printf("Remaining battery time: ");
138 	if (aip->ai_batt_time == -1)
139 		printf("unknown");
140 	else {
141 		int t, h, m, s;
142 
143 		t = aip->ai_batt_time;
144 		s = t % 60;
145 		t /= 60;
146 		m = t % 60;
147 		t /= 60;
148 		h = t;
149 		printf("%2d:%02d:%02d", h, m, s);
150 	}
151 	printf("\n");
152 	if (aip->ai_infoversion >= 1) {
153 		printf("Number of batteries: ");
154 		if (aip->ai_batteries == (u_int) -1)
155 			printf("unknown\n");
156 		else
157 			printf("%d\n", aip->ai_batteries);
158 	}
159 
160 	/*
161 	 * try to get the suspend timer
162 	 */
163 	bzero(&args, sizeof(args));
164 	args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER;
165 	args.ebx = PMDV_APMBIOS;
166 	args.ecx = 0x0001;
167 	if (ioctl(fd, APMIO_BIOS, &args)) {
168 		err(1,"Get resume timer");
169 	} else {
170 		apmerr = APMERR(args.eax);
171 		 if (apmerr == 0x0d || apmerr == 0x86)
172 			printf("Resume timer: disabled\n");
173 		else if (apmerr)
174 			fprintf(stderr,
175 			    "Failed to get the resume timer: APM error0x%x\n",
176 			    apmerr);
177 		else {
178 			/*
179 			 * OK.  We have the time (all bcd).
180 			 * CH - seconds
181 			 * DH - hours
182 			 * DL - minutes
183 			 * xh(SI) - month (1-12)
184 			 * xl(SI) - day of month (1-31)
185 			 * DI - year
186 			 */
187 			struct tm tm;
188 			char buf[1024];
189 			time_t t;
190 
191 			tm.tm_sec = bcd2int(xh(args.ecx));
192 			tm.tm_min = bcd2int(xl(args.edx));
193 			tm.tm_hour = bcd2int(xh(args.edx));
194 			tm.tm_mday = bcd2int(xl(args.esi));
195 			tm.tm_mon = bcd2int(xh(args.esi)) - 1;
196 			tm.tm_year = bcd2int(args.edi) - 1900;
197 			if (cmos_wall)
198 				t = mktime(&tm);
199 			else
200 				t = timegm(&tm);
201 			tm = *localtime(&t);
202 			strftime(buf, sizeof(buf), "%c", &tm);
203 			printf("Resume timer: %s\n", buf);
204 		}
205 	}
206 
207 	/*
208 	 * Get the ring indicator resume state
209 	 */
210 	bzero(&args, sizeof(args));
211 	args.eax  = (APM_BIOS) << 8 | APM_RESUMEONRING;
212 	args.ebx = PMDV_APMBIOS;
213 	args.ecx = 0x0002;
214 	if (ioctl(fd, APMIO_BIOS, &args) == 0) {
215 		printf("Resume on ring indicator: %sabled\n",
216 			args.ecx ? "en" : "dis");
217 	}
218 
219 	if (aip->ai_infoversion >= 1) {
220 		printf("APM Capacities:\n", aip->ai_capabilities);
221 		if (aip->ai_capabilities == 0xff00)
222 			printf("\tunknown\n");
223 		if (aip->ai_capabilities & 0x01)
224 			printf("\tglobal standby state\n");
225 		if (aip->ai_capabilities & 0x02)
226 			printf("\tglobal suspend state\n");
227 		if (aip->ai_capabilities & 0x04)
228 			printf("\tresume timer from standby\n");
229 		if (aip->ai_capabilities & 0x08)
230 			printf("\tresume timer from suspend\n");
231 		if (aip->ai_capabilities & 0x10)
232 			printf("\tRI resume from standby\n");
233 		if (aip->ai_capabilities & 0x20)
234 			printf("\tRI resume from suspend\n");
235 		if (aip->ai_capabilities & 0x40)
236 			printf("\tPCMCIA RI resume from standby\n");
237 		if (aip->ai_capabilities & 0x80)
238 			printf("\tPCMCIA RI resume from suspend\n");
239 	}
240 
241 }
242 
243 /*
244  * currently, it can turn off the display, but the display never comes
245  * back until the machine suspend/resumes :-).
246  */
247 void
248 apm_display(int fd, int newstate)
249 {
250 	if (ioctl(fd, APMIO_DISPLAY, &newstate) == -1)
251 		err(1, NULL);
252 }
253 
254 
255 void
256 apm_set_timer(int fd, int delta)
257 {
258 	time_t tmr;
259 	struct tm *tm;
260 	struct apm_bios_arg args;
261 
262 	tmr = time(NULL) + delta;
263 	if (cmos_wall)
264 		tm = localtime(&tmr);
265 	else
266 		tm = gmtime(&tmr);
267 	bzero(&args, sizeof(args));
268 	args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER;
269 	args.ebx = PMDV_APMBIOS;
270 	if (delta > 0) {
271 		args.ecx = (int2bcd(tm->tm_sec) << 8) | 0x02;
272 		args.edx = (int2bcd(tm->tm_hour) << 8) | int2bcd(tm->tm_min);
273 		args.esi = (int2bcd(tm->tm_mon + 1) << 8) | int2bcd(tm->tm_mday);
274 		args.edi = int2bcd(tm->tm_year + 1900);
275 	} else {
276 		args.ecx = 0x0000;
277 	}
278 	if (ioctl(fd, APMIO_BIOS, &args)) {
279 		err(1,"Set resume timer");
280 	}
281 }
282 
283 int
284 main(int argc, char *argv[])
285 {
286 	int	c, fd;
287 	int     sleep = 0, all_info = 1, apm_status = 0, batt_status = 0;
288 	int     display = 0, batt_life = 0, ac_status = 0, standby = 0;
289 	int	batt_time = 0, delta = 0;
290 	char	*cmdname;
291 
292 
293 	if ((cmdname = strrchr(argv[0], '/')) != NULL)
294 		cmdname++;
295 	else
296 		cmdname = argv[0];
297 
298 	if (strcmp(cmdname, "zzz") == 0) {
299 		sleep = 1;
300 		all_info = 0;
301 		goto finish_option;
302 	}
303 	while ((c = getopt(argc, argv, "ablRr:stzd:Z")) != -1) {
304 		switch (c) {
305 		case 'a':
306 			ac_status = 1;
307 			all_info = 0;
308 			break;
309 		case 'b':
310 			batt_status = 1;
311 			all_info = 0;
312 			break;
313 		case 'd':
314 			display = *optarg - '0';
315 			if (display < 0 || display > 1) {
316 				warnx("argument of option '-%c' is invalid", c);
317 				usage();
318 			}
319 			display++;
320 			all_info = 0;
321 			break;
322 		case 'l':
323 			batt_life = 1;
324 			all_info = 0;
325 			break;
326 		case 'R':
327 			delta = -1;
328 			break;
329 		case 'r':
330 			delta = atoi(optarg);
331 			break;
332 		case 's':
333 			apm_status = 1;
334 			all_info = 0;
335 			break;
336 		case 't':
337 			batt_time = 1;
338 			all_info = 0;
339 			break;
340 		case 'z':
341 			sleep = 1;
342 			all_info = 0;
343 			break;
344 		case 'Z':
345 			standby = 1;
346 			all_info = 0;
347 			break;
348 		case '?':
349 		default:
350 			usage();
351 		}
352 		argc -= optind;
353 		argv += optind;
354 	}
355 finish_option:
356 	fd = open(APMDEV, O_RDWR);
357 	if (fd == -1) {
358 		warn("can't open %s", APMDEV);
359 		return 1;
360 	}
361 	if (delta)
362 		apm_set_timer(fd, delta);
363 	if (sleep)
364 		apm_suspend(fd);
365 	else if (standby)
366 		apm_standby(fd);
367 	else if (delta == 0) {
368 		struct apm_info info;
369 
370 		apm_getinfo(fd, &info);
371 		if (all_info)
372 			print_all_info(fd, &info);
373 		if (ac_status)
374 			printf("%d\n", info.ai_acline);
375 		if (batt_status)
376 			printf("%d\n", info.ai_batt_stat);
377 		if (batt_life)
378 			printf("%d\n", info.ai_batt_life);
379 		if (apm_status)
380 			printf("%d\n", info.ai_status);
381 		if (batt_time)
382 			printf("%d\n", info.ai_batt_time);
383 		if (display)
384 			apm_display(fd, display - 1);
385 	}
386 	close(fd);
387 	return 0;
388 }
389