File: | battstat/apmlib/apmlib.c |
Warning: | line 123, column 7 File position of the stream might be 'indeterminate' after a failed operation. Can cause undefined behavior |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* apmlib.c -- Sample APM interface routines | |||
2 | * Created: Mon Jan 8 10:28:16 1996 by faith@acm.org | |||
3 | * Revised: Fri Dec 26 21:38:29 1997 by faith@acm.org | |||
4 | * Copyright 1996, 1997 Rickard E. Faith (faith@acm.org) | |||
5 | * | |||
6 | * This library is free software; you can redistribute it and/or modify it | |||
7 | * under the terms of the GNU Library General Public License as published | |||
8 | * by the Free Software Foundation; either version 2 of the License, or (at | |||
9 | * your option) any later version. | |||
10 | * | |||
11 | * This library is distributed in the hope that it will be useful, but | |||
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
14 | * Library General Public License for more details. | |||
15 | * | |||
16 | * You should have received a copy of the GNU Library General Public | |||
17 | * License along with this library; if not, write to the Free Software | |||
18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. | |||
19 | * | |||
20 | */ | |||
21 | #include <stdio.h> | |||
22 | #include <stdlib.h> | |||
23 | #include <unistd.h> | |||
24 | #include <string.h> | |||
25 | #include <fcntl.h> | |||
26 | #include <ctype.h> | |||
27 | #include <sys/stat.h> | |||
28 | #include <sys/time.h> | |||
29 | #include <sys/ioctl.h> | |||
30 | #include <sys/types.h> | |||
31 | #include <sys/sysmacros.h> | |||
32 | #include "apm.h" | |||
33 | ||||
34 | #define BACKWARD_COMPAT1 1 | |||
35 | ||||
36 | /* If APM support of the right version exists in kernel, return zero. | |||
37 | * Otherwise, return 1 if no support exists, or 2 if it is the wrong | |||
38 | * version. *NOTE* The sense of the return value is not intuitive. | |||
39 | */ | |||
40 | int apm_exists(void) | |||
41 | { | |||
42 | apm_info i; | |||
43 | ||||
44 | if (access(APM_PROC"/proc/apm", R_OK4)) | |||
45 | return 1; | |||
46 | return apm_read(&i); | |||
47 | } | |||
48 | ||||
49 | ||||
50 | /* Read information from /proc/apm. Return 0 on success, 1 if APM not | |||
51 | * installed, 2 if APM installed, but old version. | |||
52 | */ | |||
53 | int apm_read(apm_info * i) | |||
54 | { | |||
55 | FILE *str; | |||
56 | char units[10]; | |||
57 | char buffer[100]; | |||
58 | int retcode = 0; | |||
59 | ||||
60 | if (!(str = fopen(APM_PROC"/proc/apm", "r"))) | |||
61 | return 1; | |||
62 | ||||
63 | if (fgets(buffer, sizeof(buffer) - 1, str) == NULL((void*)0)) | |||
64 | printf("fgets error\n"); | |||
65 | ||||
66 | buffer[sizeof(buffer) - 1] = '\0'; | |||
67 | ||||
68 | /* Should check for other driver versions; driver 1.9 (and some | |||
69 | * others) uses this format, which doesn't expose # batteries. | |||
70 | */ | |||
71 | sscanf(buffer, "%s %d.%d %x %x %x %x %d%% %d %s\n", | |||
72 | (char *) i->driver_version, | |||
73 | &i->apm_version_major, | |||
74 | &i->apm_version_minor, | |||
75 | &i->apm_flags, | |||
76 | &i->ac_line_status, | |||
77 | &i->battery_status, | |||
78 | &i->battery_flags, | |||
79 | &i->battery_percentage, | |||
80 | &i->battery_time, | |||
81 | units); | |||
82 | i->using_minutes = !strncmp(units, "min", 3) ? 1 : 0; | |||
83 | if (i->driver_version[0] == 'B') | |||
84 | { /* old style. argh. */ | |||
85 | #if !BACKWARD_COMPAT1 | |||
86 | retcode = 2; | |||
87 | #else | |||
88 | strcpy((char *) i->driver_version, "pre-0.7"); | |||
89 | i->apm_version_major = 0; | |||
90 | i->apm_version_minor = 0; | |||
91 | i->apm_flags = 0; | |||
92 | i->ac_line_status = 0xff; | |||
93 | i->battery_status = 0xff; | |||
94 | i->battery_flags = 0xff; | |||
95 | i->battery_percentage = -1; | |||
96 | i->battery_time = -1; | |||
97 | i->using_minutes = 1; | |||
98 | ||||
99 | sscanf(buffer, "BIOS version: %d.%d", | |||
100 | &i->apm_version_major, &i->apm_version_minor); | |||
101 | ||||
102 | if (fgets(buffer, sizeof(buffer) - 1, str) == NULL((void*)0)) | |||
103 | printf("fgets error\n"); | |||
104 | ||||
105 | sscanf(buffer, "Flags: 0x%02x", &i->apm_flags); | |||
106 | if (i->apm_flags & APM_32_BIT_SUPPORT0x0002) | |||
107 | { | |||
108 | if (fgets(buffer, sizeof(buffer) - 1, str) == NULL((void*)0)) | |||
109 | printf("fgets error\n"); | |||
110 | ||||
111 | if (fgets(buffer, sizeof(buffer) - 1, str) == NULL((void*)0)) | |||
112 | printf("fgets error\n"); | |||
113 | ||||
114 | if (buffer[0] != 'P') | |||
115 | { | |||
116 | if (!strncmp(buffer + 4, "off line", 8)) | |||
117 | i->ac_line_status = 0; | |||
118 | else if (!strncmp(buffer + 4, "on line", 7)) | |||
119 | i->ac_line_status = 1; | |||
120 | else if (!strncmp(buffer + 4, "on back", 7)) | |||
121 | i->ac_line_status = 2; | |||
122 | ||||
123 | if (fgets(buffer, sizeof(buffer) - 1, str) == NULL((void*)0)) | |||
| ||||
124 | printf("fgets error\n"); | |||
125 | ||||
126 | if (!strncmp(buffer + 16, "high", 4)) | |||
127 | i->battery_status = 0; | |||
128 | else if (!strncmp(buffer + 16, "low", 3)) | |||
129 | i->battery_status = 1; | |||
130 | else if (!strncmp(buffer + 16, "crit", 4)) | |||
131 | i->battery_status = 2; | |||
132 | else if (!strncmp(buffer + 16, "charg", 5)) | |||
133 | i->battery_status = 3; | |||
134 | ||||
135 | if (fgets(buffer, sizeof(buffer) - 1, str) == NULL((void*)0)) | |||
136 | printf("fgets error\n"); | |||
137 | ||||
138 | if (strncmp(buffer + 14, "unknown", 7)) | |||
139 | i->battery_percentage = atoi(buffer + 14); | |||
140 | if (i->apm_version_major >= 1 && i->apm_version_minor >= 1) | |||
141 | { | |||
142 | if (fgets(buffer, sizeof(buffer) - 1, str) == NULL((void*)0)) | |||
143 | printf("fgets error\n"); | |||
144 | ||||
145 | sscanf(buffer, "Battery flag: 0x%02x", &i->battery_flags); | |||
146 | ||||
147 | if (fgets(buffer, sizeof(buffer) - 1, str) == NULL((void*)0)) | |||
148 | printf("fgets error\n"); | |||
149 | ||||
150 | if (strncmp(buffer + 14, "unknown", 7)) | |||
151 | i->battery_time = atoi(buffer + 14); | |||
152 | } | |||
153 | } | |||
154 | } | |||
155 | #endif | |||
156 | } | |||
157 | ||||
158 | /* Fix possible kernel bug -- percentage | |||
159 | * set to 0xff (==255) instead of -1. | |||
160 | */ | |||
161 | if (i->battery_percentage > 100) | |||
162 | i->battery_percentage = -1; | |||
163 | ||||
164 | fclose(str); | |||
165 | return retcode; | |||
166 | } | |||
167 | ||||
168 | ||||
169 | /* Lookup the device number for the apm_bios device. */ | |||
170 | dev_t apm_dev(void) | |||
171 | { | |||
172 | FILE *str; | |||
173 | static int cached = -1; | |||
174 | char buf[80]; | |||
175 | char *pt; | |||
176 | apm_info i; | |||
177 | ||||
178 | if (cached >= 0) | |||
179 | return cached; | |||
180 | ||||
181 | if (access(APM_PROC"/proc/apm", R_OK4) || apm_read(&i) == 1) | |||
182 | return cached = -1; | |||
183 | if (i.driver_version[0] == '1') | |||
184 | return cached = makedev(10, 134)gnu_dev_makedev (10, 134); | |||
185 | ||||
186 | if (!(str = fopen(APM_DEV"/proc/devices", "r"))) | |||
187 | return -1; | |||
188 | while (fgets(buf, sizeof(buf) - 1, str)) | |||
189 | { | |||
190 | buf[sizeof(buf) - 1] = '\0'; | |||
191 | for (pt = buf; *pt && isspace(*pt)((*__ctype_b_loc ())[(int) ((*pt))] & (unsigned short int ) _ISspace); ++pt); /* skip leading spaces */ | |||
192 | for (; *pt && !isspace(*pt)((*__ctype_b_loc ())[(int) ((*pt))] & (unsigned short int ) _ISspace); ++pt); /* find next space */ | |||
193 | if (isspace(*pt)((*__ctype_b_loc ())[(int) ((*pt))] & (unsigned short int ) _ISspace)) | |||
194 | { | |||
195 | *pt++ = '\0'; | |||
196 | pt[strlen(pt) - 1] = '\0'; /* get rid of newline */ | |||
197 | if (!strcmp(pt, APM_NAME"apm_bios")) | |||
198 | { | |||
199 | fclose(str); | |||
200 | return cached = makedev(atoi(buf), 0)gnu_dev_makedev (atoi(buf), 0); | |||
201 | } | |||
202 | } | |||
203 | } | |||
204 | fclose(str); | |||
205 | return cached = -1; | |||
206 | } | |||
207 | ||||
208 | ||||
209 | /* Return a file descriptor for the apm_bios device, or -1 if there is an | |||
210 | * error. Is this method secure? Should we make the device in /dev | |||
211 | * instead of /tmp? | |||
212 | * | |||
213 | * apenwarr 2001/05/11: just throw out the weird temporary device file stuff. | |||
214 | * It was only for ancient kernel versions anyway. | |||
215 | */ | |||
216 | int apm_open(void) | |||
217 | { | |||
218 | int fd; | |||
219 | apm_info i; | |||
220 | ||||
221 | if (access(APM_PROC"/proc/apm", R_OK4) || apm_read(&i) == 1) | |||
| ||||
222 | return -1; | |||
223 | if (i.driver_version[0] >= '1') | |||
224 | { | |||
225 | if ((fd = open(APM_DEVICE"/dev/apm_bios", O_RDWR02)) < 0) | |||
226 | { | |||
227 | /* Try to create it. This is reasonable | |||
228 | * for backward compatibility. | |||
229 | */ | |||
230 | if (mknod(APM_DEVICE"/dev/apm_bios", S_IFCHR0020000 | S_IRUSR0400 | S_IWUSR0200, apm_dev())) | |||
231 | { | |||
232 | unlink(APM_DEVICE"/dev/apm_bios"); | |||
233 | return -1; | |||
234 | } | |||
235 | fd = open(APM_DEVICE"/dev/apm_bios", O_RDWR02); | |||
236 | } | |||
237 | ||||
238 | return fd; | |||
239 | } | |||
240 | ||||
241 | return -1; | |||
242 | } | |||
243 | ||||
244 | ||||
245 | /* Given a file descriptor for the apm_bios device, close it. */ | |||
246 | int apm_close(int fd) | |||
247 | { | |||
248 | return close(fd); | |||
249 | } | |||
250 | ||||
251 | /* Given a file descriptor for the apm_bios device, this routine will wait | |||
252 | * timeout seconds for APM events. Up to n events will be placed in the | |||
253 | * events queue. The return code will indicate the number of events | |||
254 | * stored. Since this routine uses select(2), it will return if an | |||
255 | * unblocked signal is caught. A timeout < 0 means to block indefinately. | |||
256 | * | |||
257 | * Note that if you read a request to standby or to suspend, the kernel | |||
258 | * will be waiting for you to respond to it with a call to apm_suspend() | |||
259 | * or to apm_standby() ! | |||
260 | */ | |||
261 | int apm_get_events(int fd, int timeout, apm_event_t * events, int n) | |||
262 | { | |||
263 | int retcode; | |||
264 | fd_set fds; | |||
265 | struct timeval t; | |||
266 | ||||
267 | t.tv_sec = timeout; | |||
268 | t.tv_usec = 0; | |||
269 | ||||
270 | FD_ZERO(&fds)do { unsigned int __i; fd_set *__arr = (&fds); for (__i = 0; __i < sizeof (fd_set) / sizeof (__fd_mask); ++__i) ((__arr )->__fds_bits)[__i] = 0; } while (0); | |||
271 | FD_SET(fd, &fds)((void) (((&fds)->__fds_bits)[((fd) / (8 * (int) sizeof (__fd_mask)))] |= ((__fd_mask) (1UL << ((fd) % (8 * (int ) sizeof (__fd_mask))))))); | |||
272 | retcode = select(fd + 1, &fds, NULL((void*)0), NULL((void*)0), timeout < 0 ? NULL((void*)0) : &t); | |||
273 | if (retcode <= 0) | |||
274 | return 0; | |||
275 | return read(fd, events, n * sizeof(apm_event_t)) / sizeof(apm_event_t); | |||
276 | } | |||
277 | ||||
278 | ||||
279 | /* Try to set the Power State to Suspend. */ | |||
280 | int apm_suspend(int fd) | |||
281 | { | |||
282 | sync(); | |||
283 | return ioctl(fd, APM_IOC_SUSPEND(((0U) << (((0 +8)+8)+14)) | ((('A')) << (0 +8)) | (((2)) << 0) | ((0) << ((0 +8)+8))), NULL((void*)0)); | |||
284 | } | |||
285 | ||||
286 | ||||
287 | /* Try to set the Power State to Standby. */ | |||
288 | int apm_standby(int fd) | |||
289 | { | |||
290 | sync(); | |||
291 | return ioctl(fd, APM_IOC_STANDBY(((0U) << (((0 +8)+8)+14)) | ((('A')) << (0 +8)) | (((1)) << 0) | ((0) << ((0 +8)+8))), NULL((void*)0)); | |||
292 | } | |||
293 | ||||
294 | /* Return the last error code generated by the kernel APM driver */ | |||
295 | unsigned int apm_last_error( int fd ) | |||
296 | { | |||
297 | int err = 0; | |||
298 | ||||
299 | #ifdef APM_IOC_LAST_ERROR | |||
300 | int ierr = 0; | |||
301 | if ( (ierr = ioctl( fd, APM_IOC_LAST_ERROR, &err)) ) | |||
302 | return ierr; | |||
303 | #endif | |||
304 | return err; | |||
305 | } | |||
306 | ||||
307 | /* Define lookup table for error messages */ | |||
308 | typedef struct lookup_t { | |||
309 | int key; | |||
310 | char * msg; | |||
311 | } lookup_t; | |||
312 | ||||
313 | /* APM error messages, arranged by error code */ | |||
314 | static const lookup_t error_table[] = { | |||
315 | /* N/A { APM_SUCCESS, "Operation succeeded" }, */ | |||
316 | { APM_DISABLED0x01, "Power management disabled" }, | |||
317 | { APM_CONNECTED0x02, "Real mode interface already connected" }, | |||
318 | { APM_NOT_CONNECTED0x03, "Interface not connected" }, | |||
319 | { APM_16_CONNECTED0x05, "16 bit interface already connected" }, | |||
320 | /* N/A { APM_16_UNSUPPORTED, "16 bit interface not supported" }, */ | |||
321 | { APM_32_CONNECTED0x07, "32 bit interface already connected" }, | |||
322 | { APM_32_UNSUPPORTED0x08, "32 bit interface not supported" }, | |||
323 | { APM_BAD_DEVICE0x09, "Unrecognized device ID" }, | |||
324 | { APM_BAD_PARAM0x0a, "Parameter out of range" }, | |||
325 | { APM_NOT_ENGAGED0x0b, "Interface not engaged" }, | |||
326 | #ifdef APM_BAD_FUNCTION0x0c | |||
327 | { APM_BAD_FUNCTION0x0c, "Function not supported" }, | |||
328 | #endif | |||
329 | #ifdef APM_RESUME_DISABLED0x0d | |||
330 | { APM_RESUME_DISABLED0x0d, "Resume timer disabled" }, | |||
331 | #endif | |||
332 | { APM_BAD_STATE0x60, "Unable to enter requested state" }, | |||
333 | /* N/A { APM_NO_EVENTS, "No events pending" }, */ | |||
334 | { APM_NOT_PRESENT0x86, "No APM present" } | |||
335 | }; | |||
336 | #define ERROR_COUNT(sizeof(error_table)/sizeof(lookup_t)) (sizeof(error_table)/sizeof(lookup_t)) | |||
337 | ||||
338 | /* Return character string describing error messages from APM kernel */ | |||
339 | const char *apm_error_name( unsigned int err ) | |||
340 | { | |||
341 | int i; | |||
342 | ||||
343 | for(i=0; i<ERROR_COUNT(sizeof(error_table)/sizeof(lookup_t)); i++) | |||
344 | if(err == error_table[i].key) return(error_table[i].msg); | |||
345 | ||||
346 | return "Unknown error"; | |||
347 | } | |||
348 | ||||
349 | int apm_reject( int fd ) | |||
350 | { | |||
351 | #ifdef APM_IOC_REJECT | |||
352 | if ( ioctl( fd, APM_IOC_REJECT, NULL((void*)0) ) ) | |||
353 | return apm_last_error( fd ); | |||
354 | else | |||
355 | #endif | |||
356 | return 0; | |||
357 | } | |||
358 | ||||
359 | #ifdef APM_IOC_IGNORE /* detect kernel support of IGNORE/NOIGNORE functions */ | |||
360 | int apm_set_ignore(int fd, int mode) | |||
361 | /* Ignore Standby. */ | |||
362 | { | |||
363 | if (mode == IGNORE2) | |||
364 | { | |||
365 | printf("Telling kernel to ignore system standby/suspend mode\n"); | |||
366 | return ioctl(fd, APM_IOC_IGNORE, NULL((void*)0)); | |||
367 | } | |||
368 | else | |||
369 | { | |||
370 | printf("Telling kernel not to ignore system standby/suspend mode\n"); | |||
371 | return ioctl(fd, APM_IOC_NOIGNORE, NULL((void*)0)); | |||
372 | } | |||
373 | printf("NOTE: User-generated suspend/standby requests are not ignored\n"); | |||
374 | } | |||
375 | #endif | |||
376 | ||||
377 | /* Return a string describing the event. From p. 16 of the Intel/Microsoft | |||
378 | * Advanded Power Management (APM) BIOS Interface Specification, Revision | |||
379 | * 1.1 (September 1993). Intel Order Number: 241704-001. Microsoft Part | |||
380 | * Number: 781-110-X01. | |||
381 | * | |||
382 | * Updated to APM BIOS 1.2 spec (February 1996). Available on-line. | |||
383 | */ | |||
384 | const char *apm_event_name(apm_event_t event) | |||
385 | { | |||
386 | switch (event) | |||
387 | { | |||
388 | case APM_SYS_STANDBY0x0001: | |||
389 | return "System Standby Request"; | |||
390 | case APM_SYS_SUSPEND0x0002: | |||
391 | return "System Suspend Request"; | |||
392 | case APM_NORMAL_RESUME0x0003: | |||
393 | return "Normal Resume System"; | |||
394 | case APM_CRITICAL_RESUME0x0004: | |||
395 | return "Critical Resume System"; | |||
396 | case APM_LOW_BATTERY0x0005: | |||
397 | return "Battery Low"; | |||
398 | case APM_POWER_STATUS_CHANGE0x0006: | |||
399 | return "Power Status Change"; | |||
400 | case APM_UPDATE_TIME0x0007: | |||
401 | return "Update Time"; | |||
402 | case APM_CRITICAL_SUSPEND0x0008: | |||
403 | return "Critical Suspend"; | |||
404 | case APM_USER_STANDBY0x0009: | |||
405 | return "User System Standby Request"; | |||
406 | case APM_USER_SUSPEND0x000a: | |||
407 | return "User System Suspend Request"; | |||
408 | case APM_STANDBY_RESUME0x000b: | |||
409 | return "System Standby Resume"; | |||
410 | #ifdef APM_CAPABILITY_CHANGE0x000c | |||
411 | case APM_CAPABILITY_CHANGE0x000c: | |||
412 | return "Capability Change"; | |||
413 | #endif | |||
414 | } | |||
415 | return "Unknown"; | |||
416 | } | |||
417 | ||||
418 | ||||
419 | /* This is a convenience function that has nothing to do with APM. It just | |||
420 | * formats a time nicely. If you don't like this format, then write your | |||
421 | * own. | |||
422 | */ | |||
423 | #define SEC_PER_DAY(60*60*24) (60*60*24) | |||
424 | #define SEC_PER_HOUR(60*60) (60*60) | |||
425 | #define SEC_PER_MIN(60) (60) | |||
426 | ||||
427 | const char *apm_delta_time(time_t then, time_t now) | |||
428 | { | |||
429 | return apm_time(now - then); | |||
430 | } | |||
431 | ||||
432 | const char *apm_time(time_t t) | |||
433 | { | |||
434 | static char buffer[128]; | |||
435 | unsigned long s, m, h, d; | |||
436 | ||||
437 | d = t / SEC_PER_DAY(60*60*24); | |||
438 | t -= d * SEC_PER_DAY(60*60*24); | |||
439 | h = t / SEC_PER_HOUR(60*60); | |||
440 | t -= h * SEC_PER_HOUR(60*60); | |||
441 | m = t / SEC_PER_MIN(60); | |||
442 | t -= m * SEC_PER_MIN(60); | |||
443 | s = t; | |||
444 | ||||
445 | if (d) | |||
446 | sprintf(buffer, "%lu day%s, %02lu:%02lu:%02lu", | |||
447 | d, d > 1 ? "s" : "", h, m, s); | |||
448 | else | |||
449 | sprintf(buffer, "%02lu:%02lu:%02lu", h, m, s); | |||
450 | ||||
451 | if (t == -1) | |||
452 | sprintf(buffer, "unknown"); | |||
453 | ||||
454 | return buffer; | |||
455 | } | |||
456 | ||||
457 | const char *apm_time_nosec(time_t t) | |||
458 | { | |||
459 | static char buffer[128]; | |||
460 | unsigned long s, m, h, d; | |||
461 | ||||
462 | d = t / SEC_PER_DAY(60*60*24); | |||
463 | t -= d * SEC_PER_DAY(60*60*24); | |||
464 | h = t / SEC_PER_HOUR(60*60); | |||
465 | t -= h * SEC_PER_HOUR(60*60); | |||
466 | m = t / SEC_PER_MIN(60); | |||
467 | t -= m * SEC_PER_MIN(60); | |||
468 | s = t; | |||
469 | ||||
470 | if (s > 30) | |||
471 | ++m; | |||
472 | ||||
473 | if (d) | |||
474 | sprintf(buffer, "%lu day%s, %lu:%02lu", | |||
475 | d, d > 1 ? "s" : "", h, m); | |||
476 | else | |||
477 | sprintf(buffer, "%lu:%02lu", h, m); | |||
478 | ||||
479 | if (t == -1) | |||
480 | sprintf(buffer, "unknown"); | |||
481 | ||||
482 | return buffer; | |||
483 | } |