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