Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/usr.sbin/apm/apm.c
103477 views
1
/*
2
* APM BIOS utility for FreeBSD
3
*
4
* Copyright (C) 1994-1996 by Tatsumi Hosokawa <[email protected]>
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
#include <sys/cdefs.h>
17
#include <sys/file.h>
18
#include <sys/ioctl.h>
19
#include <sys/types.h>
20
#include <sys/sysctl.h>
21
22
#include <machine/apm_bios.h>
23
24
#include <err.h>
25
#include <stdio.h>
26
#include <stdlib.h>
27
#include <string.h>
28
#include <time.h>
29
#include <unistd.h>
30
31
#define APMDEV "/dev/apm"
32
33
#define APM_UNKNOWN 255
34
35
#define xh(a) (((a) & 0xff00) >> 8)
36
#define xl(a) ((a) & 0xff)
37
#define APMERR(a) xh(a)
38
39
static int cmos_wall = 0; /* True when wall time is in cmos clock, else UTC */
40
41
static void
42
usage(void)
43
{
44
fprintf(stderr,
45
"usage: apm [-ablstzZ] [-d enable ] [ -e enable ] "
46
"[ -h enable ] [-r delta]\n");
47
exit(1);
48
}
49
50
/*
51
* Return 1 for boolean true, and 0 for false, according to the
52
* interpretation of the string argument given.
53
*/
54
static int
55
is_true(const char *boolean)
56
{
57
char *endp;
58
long val;
59
60
val = strtoul(boolean, &endp, 0);
61
if (*endp == '\0')
62
return (val != 0 ? 1 : 0);
63
if (strcasecmp(boolean, "true") == 0 ||
64
strcasecmp(boolean, "yes") == 0 ||
65
strcasecmp(boolean, "enable") == 0)
66
return (1);
67
if (strcasecmp(boolean, "false") == 0 ||
68
strcasecmp(boolean, "no") == 0 ||
69
strcasecmp(boolean, "disable") == 0)
70
return (0);
71
/* Well, I have no idea what the user wants, so... */
72
warnx("invalid boolean argument \"%s\"", boolean);
73
usage();
74
/* NOTREACHED */
75
76
return (0);
77
}
78
79
static int
80
int2bcd(int i)
81
{
82
int retval = 0;
83
int base = 0;
84
85
if (i >= 10000)
86
return -1;
87
88
while (i) {
89
retval |= (i % 10) << base;
90
i /= 10;
91
base += 4;
92
}
93
return retval;
94
}
95
96
static int
97
bcd2int(int bcd)
98
{
99
int retval = 0;
100
int place = 1;
101
102
if (bcd > 0x9999)
103
return -1;
104
105
while (bcd) {
106
retval += (bcd & 0xf) * place;
107
bcd >>= 4;
108
place *= 10;
109
}
110
return retval;
111
}
112
113
static void
114
apm_suspend(int fd)
115
{
116
if (ioctl(fd, APMIO_SUSPEND, NULL) == -1)
117
err(1, "ioctl(APMIO_SUSPEND)");
118
}
119
120
static void
121
apm_standby(int fd)
122
{
123
if (ioctl(fd, APMIO_STANDBY, NULL) == -1)
124
err(1, "ioctl(APMIO_STANDBY)");
125
}
126
127
static void
128
apm_getinfo(int fd, apm_info_t aip)
129
{
130
if (ioctl(fd, APMIO_GETINFO, aip) == -1)
131
err(1, "ioctl(APMIO_GETINFO)");
132
}
133
134
static void
135
apm_enable(int fd, int enable)
136
{
137
if (enable) {
138
if (ioctl(fd, APMIO_ENABLE) == -1)
139
err(1, "ioctl(APMIO_ENABLE)");
140
} else {
141
if (ioctl(fd, APMIO_DISABLE) == -1)
142
err(1, "ioctl(APMIO_DISABLE)");
143
}
144
}
145
146
static void
147
print_batt_time(int batt_time)
148
{
149
printf("Remaining battery time: ");
150
if (batt_time == -1)
151
printf("unknown\n");
152
else {
153
int h, m, s;
154
155
h = batt_time;
156
s = h % 60;
157
h /= 60;
158
m = h % 60;
159
h /= 60;
160
printf("%2d:%02d:%02d\n", h, m, s);
161
}
162
}
163
164
static void
165
print_batt_life(u_int batt_life)
166
{
167
printf("Remaining battery life: ");
168
if (batt_life == APM_UNKNOWN)
169
printf("unknown\n");
170
else if (batt_life <= 100)
171
printf("%d%%\n", batt_life);
172
else
173
printf("invalid value (0x%x)\n", batt_life);
174
}
175
176
static void
177
print_batt_stat(u_int batt_stat)
178
{
179
const char *batt_msg[] = { "high", "low", "critical", "charging" };
180
181
printf("Battery Status: ");
182
if (batt_stat == APM_UNKNOWN)
183
printf("unknown\n");
184
else if (batt_stat > 3)
185
printf("invalid value (0x%x)\n", batt_stat);
186
else
187
printf("%s\n", batt_msg[batt_stat]);
188
}
189
190
static void
191
print_all_info(int fd, apm_info_t aip, int bioscall_available)
192
{
193
struct apm_bios_arg args;
194
int apmerr;
195
const char *line_msg[] = { "off-line", "on-line" , "backup power"};
196
197
printf("APM version: %d.%d\n", aip->ai_major, aip->ai_minor);
198
printf("APM Management: %s\n", aip->ai_status ? "Enabled" : "Disabled");
199
printf("AC Line status: ");
200
if (aip->ai_acline == APM_UNKNOWN)
201
printf("unknown\n");
202
else if (aip->ai_acline > 2)
203
printf("invalid value (0x%x)\n", aip->ai_acline);
204
else
205
printf("%s\n", line_msg[aip->ai_acline]);
206
207
print_batt_stat(aip->ai_batt_stat);
208
print_batt_life(aip->ai_batt_life);
209
print_batt_time(aip->ai_batt_time);
210
211
if (aip->ai_infoversion >= 1) {
212
printf("Number of batteries: ");
213
if (aip->ai_batteries == ~0U)
214
printf("unknown\n");
215
else {
216
u_int i;
217
struct apm_pwstatus aps;
218
219
printf("%d\n", aip->ai_batteries);
220
for (i = 0; i < aip->ai_batteries; ++i) {
221
bzero(&aps, sizeof(aps));
222
aps.ap_device = PMDV_BATT0 + i;
223
if (ioctl(fd, APMIO_GETPWSTATUS, &aps) == -1)
224
continue;
225
printf("Battery %d:\n", i);
226
if (aps.ap_batt_flag & APM_BATT_NOT_PRESENT) {
227
printf("not present\n");
228
continue;
229
}
230
printf("\t");
231
print_batt_stat(aps.ap_batt_stat);
232
printf("\t");
233
print_batt_life(aps.ap_batt_life);
234
printf("\t");
235
print_batt_time(aps.ap_batt_time);
236
}
237
}
238
}
239
240
if (bioscall_available) {
241
/*
242
* try to get the suspend timer
243
*/
244
bzero(&args, sizeof(args));
245
args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER;
246
args.ebx = PMDV_APMBIOS;
247
args.ecx = 0x0001;
248
if (ioctl(fd, APMIO_BIOS, &args)) {
249
printf("Resume timer: unknown\n");
250
} else {
251
apmerr = APMERR(args.eax);
252
if (apmerr == 0x0d || apmerr == 0x86)
253
printf("Resume timer: disabled\n");
254
else if (apmerr)
255
warnx(
256
"failed to get the resume timer: APM error0x%x", apmerr);
257
else {
258
/*
259
* OK. We have the time (all bcd).
260
* CH - seconds
261
* DH - hours
262
* DL - minutes
263
* xh(SI) - month (1-12)
264
* xl(SI) - day of month (1-31)
265
* DI - year
266
*/
267
struct tm tm;
268
char buf[1024];
269
time_t t;
270
271
tm.tm_sec = bcd2int(xh(args.ecx));
272
tm.tm_min = bcd2int(xl(args.edx));
273
tm.tm_hour = bcd2int(xh(args.edx));
274
tm.tm_mday = bcd2int(xl(args.esi));
275
tm.tm_mon = bcd2int(xh(args.esi)) - 1;
276
tm.tm_year = bcd2int(args.edi) - 1900;
277
if (cmos_wall)
278
t = mktime(&tm);
279
else
280
t = timegm(&tm);
281
if (t != -1) {
282
tm = *localtime(&t);
283
strftime(buf, sizeof(buf), "%c", &tm);
284
printf("Resume timer: %s\n", buf);
285
} else
286
printf("Resume timer: unknown\n");
287
}
288
}
289
290
/*
291
* Get the ring indicator resume state
292
*/
293
bzero(&args, sizeof(args));
294
args.eax = (APM_BIOS) << 8 | APM_RESUMEONRING;
295
args.ebx = PMDV_APMBIOS;
296
args.ecx = 0x0002;
297
if (ioctl(fd, APMIO_BIOS, &args) == 0) {
298
printf("Resume on ring indicator: %sabled\n",
299
args.ecx ? "en" : "dis");
300
}
301
}
302
303
if (aip->ai_infoversion >= 1) {
304
if (aip->ai_capabilities == 0xff00)
305
return;
306
printf("APM Capabilities:\n");
307
if (aip->ai_capabilities & 0x01)
308
printf("\tglobal standby state\n");
309
if (aip->ai_capabilities & 0x02)
310
printf("\tglobal suspend state\n");
311
if (aip->ai_capabilities & 0x04)
312
printf("\tresume timer from standby\n");
313
if (aip->ai_capabilities & 0x08)
314
printf("\tresume timer from suspend\n");
315
if (aip->ai_capabilities & 0x10)
316
printf("\tRI resume from standby\n");
317
if (aip->ai_capabilities & 0x20)
318
printf("\tRI resume from suspend\n");
319
if (aip->ai_capabilities & 0x40)
320
printf("\tPCMCIA RI resume from standby\n");
321
if (aip->ai_capabilities & 0x80)
322
printf("\tPCMCIA RI resume from suspend\n");
323
}
324
325
}
326
327
/*
328
* currently, it can turn off the display, but the display never comes
329
* back until the machine suspend/resumes :-).
330
*/
331
static void
332
apm_display(int fd, int newstate)
333
{
334
if (ioctl(fd, APMIO_DISPLAY, &newstate) == -1)
335
err(1, "ioctl(APMIO_DISPLAY)");
336
}
337
338
static void
339
apm_haltcpu(int fd, int enable)
340
{
341
if (enable) {
342
if (ioctl(fd, APMIO_HALTCPU, NULL) == -1)
343
err(1, "ioctl(APMIO_HALTCPU)");
344
} else {
345
if (ioctl(fd, APMIO_NOTHALTCPU, NULL) == -1)
346
err(1, "ioctl(APMIO_NOTHALTCPU)");
347
}
348
}
349
350
static void
351
apm_set_timer(int fd, int delta)
352
{
353
time_t tmr;
354
struct tm *tm;
355
struct apm_bios_arg args;
356
357
tmr = time(NULL) + delta;
358
if (cmos_wall)
359
tm = localtime(&tmr);
360
else
361
tm = gmtime(&tmr);
362
bzero(&args, sizeof(args));
363
args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER;
364
args.ebx = PMDV_APMBIOS;
365
if (delta > 0) {
366
args.ecx = (int2bcd(tm->tm_sec) << 8) | 0x02;
367
args.edx = (int2bcd(tm->tm_hour) << 8) | int2bcd(tm->tm_min);
368
args.esi = (int2bcd(tm->tm_mon + 1) << 8) | int2bcd(tm->tm_mday);
369
args.edi = int2bcd(tm->tm_year + 1900);
370
} else {
371
args.ecx = 0x0000;
372
}
373
if (ioctl(fd, APMIO_BIOS, &args)) {
374
err(1,"set resume timer");
375
}
376
}
377
378
int
379
main(int argc, char *argv[])
380
{
381
int c, fd;
382
int dosleep = 0, all_info = 1, apm_status = 0, batt_status = 0;
383
int display = -1, batt_life = 0, ac_status = 0, standby = 0;
384
int batt_time = 0, delta = 0, enable = -1, haltcpu = -1;
385
int bioscall_available = 0;
386
size_t cmos_wall_len = sizeof(cmos_wall);
387
388
if (sysctlbyname("machdep.wall_cmos_clock", &cmos_wall, &cmos_wall_len,
389
NULL, 0) == -1)
390
err(1, "sysctlbyname(machdep.wall_cmos_clock)");
391
392
while ((c = getopt(argc, argv, "abe:h:lRr:stzd:Z")) != -1) {
393
switch (c) {
394
case 'a':
395
ac_status = 1;
396
all_info = 0;
397
break;
398
case 'b':
399
batt_status = 1;
400
all_info = 0;
401
break;
402
case 'd':
403
display = is_true(optarg);
404
all_info = 0;
405
break;
406
case 'l':
407
batt_life = 1;
408
all_info = 0;
409
break;
410
case 'R':
411
delta = -1;
412
break;
413
case 'r':
414
delta = atoi(optarg);
415
break;
416
case 's':
417
apm_status = 1;
418
all_info = 0;
419
break;
420
case 'e':
421
enable = is_true(optarg);
422
all_info = 0;
423
break;
424
case 'h':
425
haltcpu = is_true(optarg);
426
all_info = 0;
427
break;
428
case 't':
429
batt_time = 1;
430
all_info = 0;
431
break;
432
case 'z':
433
dosleep = 1;
434
all_info = 0;
435
break;
436
case 'Z':
437
standby = 1;
438
all_info = 0;
439
break;
440
case '?':
441
default:
442
usage();
443
}
444
argc -= optind;
445
argv += optind;
446
}
447
if (haltcpu != -1 || enable != -1 || display != -1 || delta || dosleep
448
|| standby) {
449
fd = open(APMDEV, O_RDWR);
450
bioscall_available = 1;
451
} else if ((fd = open(APMDEV, O_RDWR)) >= 0)
452
bioscall_available = 1;
453
else
454
fd = open(APMDEV, O_RDONLY);
455
if (fd == -1)
456
err(1, "can't open %s", APMDEV);
457
if (enable != -1)
458
apm_enable(fd, enable);
459
if (haltcpu != -1)
460
apm_haltcpu(fd, haltcpu);
461
if (delta)
462
apm_set_timer(fd, delta);
463
if (dosleep)
464
apm_suspend(fd);
465
else if (standby)
466
apm_standby(fd);
467
else if (delta == 0) {
468
struct apm_info info;
469
470
apm_getinfo(fd, &info);
471
if (all_info)
472
print_all_info(fd, &info, bioscall_available);
473
if (ac_status)
474
printf("%d\n", info.ai_acline);
475
if (batt_status)
476
printf("%d\n", info.ai_batt_stat);
477
if (batt_life)
478
printf("%d\n", info.ai_batt_life);
479
if (apm_status)
480
printf("%d\n", info.ai_status);
481
if (batt_time)
482
printf("%d\n", info.ai_batt_time);
483
if (display != -1)
484
apm_display(fd, display);
485
}
486
close(fd);
487
exit(0);
488
}
489
490