1 /*
2 * Copyright (C) 2006 Johannes Hausensteiner (johau@gmx.net)
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19 /*
20 * Filename: data.c
21 *
22 * Function: retrieve battery data from the OS
23 *
24 * Part of: batteryCat application
25 *
26 * Changelog
27 *
28 * 0.1 21.Aug.2011 JH new
29 * 0.3 31.Aug.2011 JH add apm, apm_test cmdline parameters
30 * 0.3 14.Sep.2011 JH voltage parameter in GetBatteryState()
31 * 0.7 22.Jan.2012 JH add compatibility with FreeBSD, Mac OSX
32 * 17.Jul.2012 JH Change GetBatteryState() f better compat w Win9x
33 * 1.3 27.Apr.2013 JH check for fopen() errors
34 * 1.4 20.Aug.2015 JH support /sys/class/power_supply/BAT0
35 * 1.5 28.Jul.2017 JH read unit (mAh/mWh) from /proc/acpi/.../info
36 * fix /sys/.../AC/online vs. /sys/.../AC0/online
37 * 1.6 4.Feb.2019 JH fix IOCTL_BATTERY_QUERY_TAG fails (win32)
38 */
39
40
41 #ifdef WIN32
42 #include <windows.h>
43 #include <setupapi.h>
44 #include <stdio.h>
45 #include <initguid.h>
46 #include <ddk/ntddk.h>
47 #include <ddk/batclass.h>
48 #else
49 #include <unistd.h> /* access(), F_OK */
50 #endif
51
52 #include <stdlib.h>
53 #include <string.h>
54 #include <ctype.h>
55
56 #include "batterycat.h"
57
58 #ifdef __APPLE__
59 #include "gettick.h"
60 #define SYSTEM_PROFILER_CMD "system_profiler SPPowerDataType"
61 #endif
62
63 #ifdef __FreeBSD__
64 #include "gettick.h"
65 #define ACPICONF_CMD "acpiconf -i 0"
66 #endif
67
68 /****************************** Defines *********************************/
69 #define BUFLEN 100
70 #define OK 0
71
72
73 #define FOUND_FULL 0x01
74 #define FOUND_REMAINING 0x02
75 #define FOUND_CYCLES 0x04
76 #define FOUND_CONNECTED 0x08
77 #define FOUND_CHARGING 0x10
78 #define FOUND_ALL \
79 (FOUND_FULL | FOUND_REMAINING | FOUND_CYCLES | FOUND_CONNECTED | FOUND_CHARGING)
80
81 #define FOUND_DESIGN FOUND_CYCLES
82
83 /****************************** Macros **********************************/
84 #define DEBUG(x) if (debug) printf x /* a simple trace macro */
85
86 /****************************** Structs *********************************/
87
88 /************************** Private Functions ***************************/
89 #ifdef WIN32
90 static DWORD GetBatteryState(/* get information from the battery */
91 BATTERY_INFORMATION *bi); /* out: the BATTERY_INFORMATION struct */
92
93 static void print_batinfo (BATTERY_INFORMATION *bi);
94
95 static void print_sps (SYSTEM_POWER_STATUS *sps);
96 #endif
97 void print_battery_data(struct BATTERY_DATA *bd);
98
99 /****************************** Globals *********************************/
100 extern int debug;
101 extern int apm;
102 extern int apm_test;
103
104 extern struct BATTERY_DATA battery_data;
105
106 /* data_has_extended */
107 /**************************************************************************/
data_has_extended()108 int data_has_extended () /* collect the battery data */
109 /***************************************************************************
110 * globals : *
111 * called from : *
112 * calls : *
113 * *
114 * Function *
115 * -------- *
116 * Check whether our system provides extended data. *
117 * *
118 * Returns *
119 * ------- *
120 * 0 .. no extended data available *
121 * 1 .. yes, extended data available *
122 ***************************************************************************/
123 {
124 #ifdef WIN32
125 int ret;
126 BATTERY_INFORMATION bi;
127
128 ret = GetBatteryState(&bi);
129 DEBUG(("data_has_extended() returns: %d\n", ret));
130
131 return ret;
132 #endif
133 #ifdef __linux__
134 int ret;
135
136 ret = 0;
137 if ( !apm
138 && !apm_test
139 && ( access("/proc/acpi/battery/BAT0/info", R_OK)==OK
140 || access("/sys/class/power_supply/BAT0", F_OK)==OK)
141 )
142 ret = 1;
143
144 return ret;
145 #endif
146 #ifdef __APPLE__
147 static int cache = -1;
148 int ret, found;
149 FILE *stream;
150 const char *cmd=SYSTEM_PROFILER_CMD;
151 char line[BUFLEN];
152
153 DEBUG(("data_has_extended()\n"));
154 if (cache == -1)
155 {
156 ret = 0;
157 found = 0;
158 if (!apm)
159 {
160 /* check for presence of the 'system_profiler' cmd and for the keywords
161 * we want to use in the output
162 */
163 stream = popen (cmd, "r");
164 if (stream)
165 {
166 while (1)
167 {
168 fgets (line, sizeof(line), stream);
169 if (feof(stream))
170 break;
171
172 if (strstr (line, "Full") && strstr (line, "apacity"))
173 found |= FOUND_FULL;
174
175 if (strstr (line, "emaining"))
176 found |= FOUND_REMAINING;
177
178 if (strstr (line, "Cycle Count"))
179 found |= FOUND_CYCLES;
180
181 if (strstr (line, "Connected"))
182 found |= FOUND_CONNECTED;
183
184 if (strstr (line, "Charging"))
185 found |= FOUND_CHARGING;
186 }
187 pclose (stream);
188 if (found == FOUND_ALL)
189 ret = 1;
190 cache = ret;
191 }
192 } /* !apm */
193 } else /* first time */
194 ret = cache;
195
196 DEBUG(("data_has_extended() returns: %d\n", ret));
197 return ret;
198 #endif
199 #ifdef __FreeBSD__
200 static int cache = -1;
201 int ret, found;
202 FILE *stream;
203 const char *cmd=ACPICONF_CMD;
204 char line[BUFLEN];
205
206 DEBUG(("data_has_extended()\n"));
207 if (cache == -1)
208 {
209 ret = 0;
210 found = 0;
211 if (!apm)
212 {
213 /* check for presence of the 'acpiconf' cmd and for the keywords
214 * we want to use in the output
215 */
216 stream = popen (cmd, "r");
217 if (stream)
218 {
219 while (1)
220 {
221 fgets (line, sizeof(line), stream);
222 if (feof(stream))
223 break;
224
225 if (strstr (line, "Design capacity"))
226 found |= FOUND_DESIGN;
227
228 if (strstr (line, "Last full capacity"))
229 found |= FOUND_FULL;
230
231 if (strstr (line, "Remaining capacity"))
232 found |= FOUND_REMAINING;
233
234 if (strstr (line, "State"))
235 found |= FOUND_CONNECTED | FOUND_CHARGING;
236 }
237 pclose (stream);
238 if (found == FOUND_ALL)
239 ret = 1;
240 cache = ret;
241 }
242 } /* !apm */
243 } else /* first time */
244 ret = cache;
245
246 DEBUG(("data_has_extended() returns: %d\n", ret));
247 return ret;
248 #endif
249 }
250
251 /* data_collect */
252 /**************************************************************************/
data_collect(struct BATTERY_DATA * data)253 int data_collect ( /* collect the battery data */
254 struct BATTERY_DATA *data) /* */
255 /***************************************************************************
256 * globals : *
257 * called from : *
258 * calls : *
259 * *
260 * Function *
261 * -------- *
262 * Collect the battery data. *
263 * *
264 * Returns *
265 * ------- *
266 * *
267 ***************************************************************************/
268 {
269 #ifdef WIN32
270 int ret;
271 SYSTEM_POWER_STATUS sps;
272 BATTERY_INFORMATION bi;
273 BOOL res;
274
275 DEBUG (("data_collect()\n"));
276
277 ret = -1;
278
279 res = GetSystemPowerStatus (&sps);
280 if (res)
281 {
282 if (debug > 1)
283 print_sps (&sps);
284
285 res = GetBatteryState(&bi);
286 if (res)
287 {
288 if (debug > 1)
289 print_batinfo (&bi);
290
291 data->extended_info = 1;
292 if (data->custom_cap)
293 data->capacity_max = data->custom_cap_value;
294 else
295 data->capacity_max = bi.DesignedCapacity;
296 data->cycles = bi.CycleCount;
297 data->capacity_cur = bi.FullChargedCapacity;
298 data->charge_cur = (data->capacity_cur * sps.BatteryLifePercent) / 100;
299 data->unit = unit_name[UNIT_mWh];
300 } else
301 {
302 /* no extended battery information available; fall back to APM-style */
303 DEBUG (("data_collect(): GetBatteryStatus() returns error\n"));
304
305 if (data->custom_cap)
306 data->capacity_max = data->custom_cap_value;
307 else
308 data->capacity_max = 100;
309 data->cycles = 0;
310 if ((long int)sps.BatteryLifeTime >= 0)
311 {
312 sps.BatteryLifeTime /= 60;
313 data->capacity_cur = (sps.BatteryLifeTime * 100) / sps.BatteryLifePercent;
314 data->charge_cur = sps.BatteryLifeTime;
315 data->unit = unit_name[UNIT_min];
316 } else
317 {
318 data->capacity_cur = data->capacity_max;
319 data->charge_cur = (data->capacity_cur * sps.BatteryLifePercent) / 100;
320 if (data->custom_cap)
321 data->unit = unit_name[UNIT_mWh];
322 else
323 data->unit = unit_name[UNIT_percent];
324 }
325 }
326 data->charge_max = data->capacity_cur;
327 if (sps.ACLineStatus == 1)
328 data->charger_connected = 1;
329 else
330 data->charger_connected = 0;
331 if (sps.BatteryFlag & 8)
332 data->is_charging = 1;
333 else
334 data->is_charging = 0;
335
336 if (debug > 1)
337 print_battery_data (data);
338
339 ret = 0;
340 } else
341 {
342 DEBUG (("data_collect(): GetSystemPowerStatus() returns error (%ld)\n",
343 GetLastError()));
344 }
345
346 return ret;
347 #endif /* WIN32 */
348 #ifdef __linux__
349 int ret;
350 FILE *fp;
351 char *filename;
352 char buf[BUFLEN];
353 int acline, batt_stat, batt_percent, batt_time;
354 char batt_unit[12];
355 char *p;
356
357 DEBUG (("data_collect()\n"));
358
359 ret = -1;
360 /* check for ACPI */
361 if (data_has_extended())
362 {
363 data->extended_info = 1;
364 filename = "/proc/acpi/battery/BAT0/info";
365 fp = fopen (filename, "r");
366 if (fp)
367 {
368 DEBUG (("data_collect(): looking in /proc/acpi/battery\n"));
369 data->unit = unit_name[UNIT_mAh];
370 while (1)
371 {
372 fgets (buf, sizeof(buf), fp);
373 if (feof(fp))
374 break;
375 buf[strlen(buf)-1] = '\0';
376 p = buf;
377 if (data->custom_cap)
378 {
379 data->capacity_max = data->custom_cap_value;
380 } else
381 {
382 if (strstr(buf, "design capacity:"))
383 {
384 while (!isdigit(*p)) p++;
385 data->capacity_max = atoi (p);
386 }
387 }
388 if (strstr(buf, "last full capacity"))
389 {
390 while (!isdigit(*p)) p++;
391 data->capacity_cur = atoi (p);
392 while (isdigit(*p)) p++;
393 while (isspace(*p)) p++;
394 if (strncasecmp(p, "mWh", 3) == 0)
395 data->unit = unit_name[UNIT_mWh];
396 }
397 if (strstr(buf, "cycle count"))
398 {
399 while (!isdigit(*p)) p++;
400 data->cycles = atoi (p);
401 }
402 }
403 fclose (fp);
404 filename = "/proc/acpi/battery/BAT0/state";
405 fp = fopen (filename, "r");
406 if (fp == NULL)
407 {
408 perror (filename);
409 return -1;
410 }
411 while (1)
412 {
413 fgets (buf, sizeof(buf), fp);
414 if (feof(fp))
415 break;
416 buf[strlen(buf)-1] = '\0';
417 p = buf;
418 if (strstr(buf, "charging state"))
419 {
420 while (*p != ':') p++;
421 p ++;
422 while (isspace(*p)) p++;
423 if (strcmp (p, "charging") == 0)
424 data->is_charging = 1;
425 else
426 data->is_charging = 0;
427 }
428 if (strstr(buf, "remaining capacity"))
429 {
430 while (!isdigit(*p)) p++;
431 data->charge_cur = atoi (p);
432 if (data->charge_cur > data->capacity_cur)
433 data->charge_cur = data->capacity_cur;
434 }
435 }
436 fclose (fp);
437 filename = "/proc/acpi/ac_adapter/AC/state";
438 fp = fopen (filename, "r");
439 if (fp == NULL)
440 {
441 /* had seen this on Ubuntu ... */
442 fp = fopen ("/proc/acpi/ac_adapter/AC0/state", "r");
443 if (fp == NULL)
444 {
445 perror (filename);
446 return -1;
447 }
448 }
449 data->charger_connected = 0;
450 while (1)
451 {
452 fgets (buf, sizeof(buf), fp);
453 if (feof(fp))
454 break;
455 buf[strlen(buf)-1] = '\0';
456 p = buf;
457 if (strstr(buf, "state"))
458 {
459 while (*p != ':') p++;
460 p ++;
461 while (isspace(*p)) p++;
462 if (strcmp (p, "on-line") == 0)
463 data->charger_connected = 1;
464 }
465 }
466 fclose (fp);
467 } else
468 {
469 DEBUG (("data_collect(): looking in /sys/class/power_supply/BAT0\n"));
470 data->unit = unit_name[UNIT_mAh];
471 filename = "/sys/class/power_supply/BAT0/charge_full_design";
472 fp = fopen (filename, "r");
473 if (fp == NULL)
474 {
475 /* apparently on some systems it is energy_* instead of charge_* */
476 filename = "/sys/class/power_supply/BAT0/energy_full_design";
477 fp = fopen (filename, "r");
478 if (fp == NULL)
479 {
480 perror (filename);
481 return -1;
482 }
483 data->unit = unit_name[UNIT_mWh];
484 }
485 fgets (buf, sizeof(buf), fp);
486 fclose (fp);
487 data->capacity_max = atoi (buf) / 1000;
488
489 filename = "/sys/class/power_supply/BAT0/charge_full";
490 fp = fopen (filename, "r");
491 if (fp == NULL)
492 {
493 filename = "/sys/class/power_supply/BAT0/energy_full";
494 fp = fopen (filename, "r");
495 if (fp == NULL)
496 {
497 perror (filename);
498 return -1;
499 }
500 }
501 fgets (buf, sizeof(buf), fp);
502 fclose (fp);
503 data->capacity_cur = atoi (buf) / 1000;
504
505 filename = "/sys/class/power_supply/BAT0/charge_now";
506 fp = fopen (filename, "r");
507 if (fp == NULL)
508 {
509 filename = "/sys/class/power_supply/BAT0/energy_now";
510 fp = fopen (filename, "r");
511 if (fp == NULL)
512 {
513 perror (filename);
514 return -1;
515 }
516 }
517 fgets (buf, sizeof(buf), fp);
518 fclose (fp);
519 data->charge_cur = atoi (buf) / 1000;
520
521 filename = "/sys/class/power_supply/BAT0/cycle_count";
522 fp = fopen (filename, "r");
523 if (fp == NULL)
524 {
525 perror (filename);
526 return -1;
527 }
528 fgets (buf, sizeof(buf), fp);
529 fclose (fp);
530 data->cycles = atoi (buf);
531
532 filename = "/sys/class/power_supply/BAT0/status";
533 fp = fopen (filename, "r");
534 if (fp == NULL)
535 {
536 perror (filename);
537 return -1;
538 }
539 fgets (buf, sizeof(buf), fp);
540 fclose (fp);
541 if (strncasecmp (buf, "charging", 8) == 0)
542 data->is_charging = 1;
543 else
544 data->is_charging = 0;
545
546 filename = "/sys/class/power_supply/AC/online";
547 fp = fopen (filename, "r");
548 if (fp == NULL)
549 {
550 fp = fopen ("/sys/class/power_supply/AC0/online", "r");
551 if (fp == NULL)
552 {
553 perror (filename);
554 return -1;
555 }
556 }
557 fgets (buf, sizeof(buf), fp);
558 fclose (fp);
559 data->charger_connected = atoi (buf);
560 }
561 data->charge_max = data->capacity_cur;
562 if (debug > 1)
563 print_battery_data (data);
564 ret = 0;
565 } else
566 {
567 /* not found, check for APM */
568 if (apm_test)
569 filename = "/tmp/apm";
570 else
571 filename = "/proc/apm";
572
573 fp = fopen (filename, "r");
574 if (fp)
575 {
576 fgets (buf, sizeof(buf), fp);
577 buf[strlen(buf)-1] = '\0';
578 fclose (fp);
579 /* the apm line looks like this:
580 * 1.16 1.2 0x03 0x00 0x00 0x01 88% -1 ?
581 * APM driver version
582 * APM BIOS version: 1.0, 1.1 or 1.2
583 * APM flags from APM Installation Check (0x00):
584 * bit 0: APM_16_BIT_SUPPORT
585 * bit 1: APM_32_BIT_SUPPORT
586 * bit 2: APM_IDLE_SLOWS_CLOCK
587 * bit 3: APM_BIOS_DISABLED
588 * bit 4: APM_BIOS_DISENGAGED
589 * AC connected: 0=offline, 1=online, 2=backup, 255=unknown
590 * battery status:
591 * 0: high
592 * 1: low
593 * 2: critical
594 * 3: charging
595 * 4: not present
596 * 255: unknown
597 * battery flags:
598 * bit 0: High
599 * bit 1: Low
600 * bit 2: Critical
601 * bit 3: Charging
602 * bit 7: No system battery
603 * 0xff: Unknown
604 * battery percent (remaining battery life % of charge):
605 * 0-100: valid
606 * -1: Unknown
607 * battery time (remaining battery life time units):
608 * time units:
609 * "min": minutes
610 * "sec": seconds
611 * "?": unknown
612 */
613 if (debug > 1)
614 printf ("data_collect(): /proc/apm=%s\n", buf);
615 if (sscanf(buf, "%*s %*d.%*d %*x %x %x %*x %d%% %d %s",
616 &acline, &batt_stat, &batt_percent, &batt_time, batt_unit))
617 {
618 if (data->custom_cap)
619 data->capacity_max = data->custom_cap_value;
620 else
621 data->capacity_max = 100;
622 data->cycles = 0;
623 if (batt_time >= 0)
624 {
625 if (strncmp(batt_unit, "min", 3))
626 batt_time /= 60;
627 data->capacity_cur = (batt_time * 100) / batt_percent;
628 data->charge_cur = batt_time;
629 data->unit = unit_name[UNIT_min];
630 } else
631 {
632 data->capacity_cur = data->capacity_max;
633 data->charge_cur = (data->capacity_cur * batt_percent) / 100;
634 if (data->custom_cap)
635 data->unit = unit_name[UNIT_mAh];
636 else
637 data->unit = unit_name[UNIT_percent];
638 }
639 data->charge_max = data->capacity_cur;
640 if (acline == 1)
641 data->charger_connected = 1;
642 else
643 data->charger_connected = 0;
644 if (batt_stat == 3)
645 data->is_charging = 1;
646 else
647 data->is_charging = 0;
648 } else {
649 data->charge_cur = 0;
650 data->capacity_cur = 0;
651 data->charge_max = data->capacity_cur;
652 data->capacity_max = 0;
653 data->cycles = 0;
654 data->charger_connected = 0;
655 data->is_charging = 0;
656 data->extended_info = 0;
657 data->unit = unit_name[UNIT_min];
658 }
659 ret = 0;
660 } else
661 {
662 perror (filename);
663 }
664 }
665 return ret;
666 #endif /* __linux__ */
667 #ifdef __APPLE__
668 int ret;
669 FILE *stream;
670 const char *cmd=SYSTEM_PROFILER_CMD;
671 char line[BUFLEN];
672 char *p;
673
674 DEBUG (("data_collect()\n"));
675
676 ret = -1;
677 /* check for extended information available */
678 if (data_has_extended())
679 {
680 /* issue the 'system_profiler' cmd and read the output */
681 stream = popen (cmd, "r");
682 if (stream == NULL)
683 {
684 perror (cmd);
685 return -1;
686 }
687
688 /*
689 * Now read the output. These are the relevant lines:
690 *
691 * -------------------------------------
692 *
693 * Battery Information:
694 *
695 * Battery Installed: Yes
696 * First low level warning: No
697 * Full Charge Capacity (mAh): 4273
698 * Remaining Capacity (mAh): 4266
699 * Amperage (mA): 0
700 * Voltage (mV): 16780
701 * Cycle Count: 12
702 *
703 * AC Charger Information:
704 *
705 * AC Charger (Watts): 50
706 * Connected: Yes
707 * Charging: No
708 *
709 * -------------------------------------
710 *
711 * Unfortunately there is no information about the battery design capacity.
712 * This means that the user has to enter the design cap. manually.
713 */
714
715 while (1)
716 {
717 fgets (line, sizeof(line), stream);
718 if (feof(stream))
719 break;
720
721 p = strstr (line, "Full");
722 if (p)
723 {
724 p = strstr (line, "apacity");
725 if (p)
726 {
727 while (!isdigit(*p)) p ++;
728 data->capacity_cur = atoi (p);
729 }
730 }
731
732 p = strstr (line, "emaining");
733 if (p)
734 {
735 while (!isdigit(*p)) p++;
736 data->charge_cur = atoi (p);
737 }
738
739 p = strstr (line, "Cycle Count");
740 if (p)
741 {
742 while (!isdigit(*p)) p++;
743 data->cycles = atoi (p);
744 }
745
746 p = strstr (line, "Connected");
747 if (p)
748 {
749 if (strstr(p, "Yes"))
750 data->charger_connected = 1;
751 else
752 data->charger_connected = 0;
753 }
754
755 p = strstr (line, "Charging");
756 if (p)
757 {
758 if (strstr(p, "Yes"))
759 data->is_charging = 1;
760 else
761 data->is_charging = 0;
762 }
763
764 if (data->custom_cap)
765 data->capacity_max = data->custom_cap_value;
766 else
767 data->capacity_max = 0;
768
769 data->charge_max = data->capacity_cur;
770 data->extended_info = 1;
771 data->unit = unit_name[UNIT_mAh];
772 }
773 pclose (stream);
774 ret = 0;
775 }
776 return ret;
777 #endif /* __APPLE__ */
778 #ifdef __FreeBSD__
779 int ret;
780 FILE *stream;
781 const char *cmd=ACPICONF_CMD;
782 char line[BUFLEN];
783 char *p;
784 int batt_percent;
785
786 DEBUG (("data_collect()\n"));
787
788 ret = -1;
789 /* check for extended information available */
790 if (data_has_extended())
791 {
792 /* issue the 'acpiconf' cmd and read the output */
793 stream = popen (cmd, "r");
794 if (stream == NULL)
795 {
796 perror (cmd);
797 return -1;
798 }
799
800 /*
801 * This is a sample output:
802 *
803 * Design capacity: 4400 mAh
804 * Last full capacity: 2996 mAh
805 * Technology: secondary (rechargeable)
806 * Design voltage: 10800 mV
807 * Capacity (warn): 440 mAh
808 * Capacity (low): 133 mAh
809 * Low/warn granularity: 44 mAh
810 * Warn/full granularity: 44 mAh
811 * Model number: DELL 62
812 * Serial number: 805
813 * Type: LION
814 * OEM info: Sanyo
815 * State: charging / discharging / high
816 * Remaining capacity: 99%
817 * Remaining time: unknown
818 * Present rate: 221 mA (2773 mW)
819 * Present voltage: 12551 mV
820 */
821
822 while (1)
823 {
824 fgets (line, sizeof(line), stream);
825 if (feof(stream))
826 break;
827
828 if (data->custom_cap)
829 data->capacity_max = data->custom_cap_value;
830 else
831 {
832 p = strstr (line, "Design capacity");
833 if (p)
834 {
835 while (!isdigit(*p)) p++;
836 data->capacity_max = atoi (p);
837 }
838 }
839 p = strstr (line, "Last full capacity");
840 if (p)
841 {
842 while (!isdigit(*p)) p++;
843 data->capacity_cur = atoi (p);
844 }
845 p = strstr (line, "Remaining capacity");
846 if (p)
847 {
848 while (!isdigit(*p)) p++;
849 batt_percent = atoi (p);
850 data->charge_cur = (data->capacity_cur * batt_percent) / 100;
851 }
852 p = strstr (line, "State");
853 if (p)
854 {
855 p = strstr (line, "high");
856 if (p)
857 {
858 data->charger_connected = 1;
859 data->is_charging = 0;
860 } else
861 {
862 p = strstr (line, "discharging");
863 if (p)
864 {
865 data->charger_connected = 0;
866 data->is_charging = 0;
867 } else
868 {
869 data->charger_connected = 1;
870 data->is_charging = 1;
871 }
872 }
873 }
874
875 data->charge_max = data->capacity_cur;
876 data->cycles = 0;
877 data->extended_info = 1;
878 data->unit = unit_name[UNIT_mAh];
879 }
880 pclose (stream);
881 ret = 0;
882 } else /* has ACPI */
883 {
884 /* issue the 'apm' cmd and read the output */
885 stream = popen ("apm", "r");
886 if (stream == NULL)
887 {
888 perror (cmd);
889 return -1;
890 }
891 /*
892 * This is a sample output:
893 *
894 * APM version: 1.2
895 * APM Management: Disabled
896 * AC Line status: on-line
897 * Battery Status: charging / discharging / high
898 * Remaining battery life: 99%
899 * Remaining battery time: unknown
900 * Number of batteries: 2
901 * Battery 0:
902 * Battery Status: charging
903 * Remaining battery life: 99%
904 * Remaining battery time: unknown
905 * Battery 1:
906 * not present
907 *
908 */
909
910 if (data->custom_cap)
911 data->capacity_max = data->custom_cap_value;
912 else
913 data->capacity_max = 100;
914 while (1)
915 {
916 fgets (line, sizeof(line), stream);
917 if (feof(stream))
918 break;
919
920 p = strstr (line, "life");
921 if (p)
922 {
923 while (!isdigit(*p)) p++;
924 batt_percent = atoi (p);
925 data->capacity_cur = data->capacity_max;
926 data->charge_cur = (data->capacity_cur * batt_percent) / 100;
927 if (data->custom_cap)
928 data->unit = unit_name[UNIT_mAh];
929 else
930 data->unit = unit_name[UNIT_percent];
931 }
932 p = strstr (line, "AC Line");
933 if (p)
934 {
935 if (strstr(line, "on-line"))
936 data->charger_connected = 1;
937 else
938 data->charger_connected = 0;
939 }
940 p = strstr (line, "Battery Status");
941 if (p)
942 {
943 if (strstr(line, "discharging") || strstr(line, "high"))
944 data->is_charging = 0;
945 else
946 data->is_charging = 1;
947 }
948 }
949 pclose (stream);
950 data->cycles = 0;
951 data->charge_max = data->capacity_cur;
952
953 ret = 0;
954 }
955 return ret;
956 #endif /* __FreeBSD__ */
957 #if 0
958 /* battery data simulation */
959 static unsigned long start;
960
961 DEBUG (("data_collect()\n"));
962
963 if (start == 0L)
964 {
965 /* first time called, init with start values */
966 start = GetTickCount();
967 if (data->custom_cap)
968 data->capacity_max = data->custom_cap_value;
969 else
970 data->capacity_max = 5000;
971 data->capacity_cur = (data->capacity_max * 93) / 100;
972 data->cycles = 0;
973 }
974
975 if (data->is_charging)
976 {
977 /* simulate charging */
978 data->charge_cur = (GetTickCount() - start) / 100;
979 if (data->charge_cur >= data->capacity_cur)
980 {
981 /* full => discharge */
982 data->charge_cur = data->capacity_cur;
983 if (data->capacity_cur > 420)
984 /* degrade battery */
985 data->capacity_cur = (data->capacity_cur * 99) / 100;
986 data->cycles ++;
987 data->charger_connected = 0;
988 data->is_charging = 0;
989 start = GetTickCount();
990 }
991 } else
992 {
993 /* simulate discharging */
994 data->charge_cur = data->capacity_cur - ((GetTickCount() - start) / 100);
995 if (data->charge_cur < 0)
996 {
997 /* totally empty => charge */
998 data->charge_cur = 0;
999 data->charger_connected = 1;
1000 data->is_charging = 1;
1001 start = GetTickCount();
1002 }
1003 }
1004 data->charge_max = data->capacity_cur;
1005 data->extended_info = 1;
1006 data->unit = unit_name[UNIT_mAh];
1007
1008 return 0;
1009 #endif
1010 }
1011
1012 /* data_get_capacity */
1013 /**************************************************************************/
data_get_capacity()1014 int data_get_capacity () /* read the current max capacity */
1015 /***************************************************************************
1016 * globals : *
1017 * called from : *
1018 * calls : *
1019 * *
1020 * Function *
1021 * -------- *
1022 * *
1023 * *
1024 * Returns *
1025 * ------- *
1026 * *
1027 ***************************************************************************/
1028 {
1029 DEBUG (("data_get_capacity() returns: %d\n", battery_data.capacity_cur));
1030 return battery_data.capacity_cur;
1031 }
1032
1033 /* data_get_original_capacity */
1034 /**************************************************************************/
data_get_original_capacity()1035 int data_get_original_capacity () /* read the original capacity */
1036 /***************************************************************************
1037 * globals : *
1038 * called from : *
1039 * calls : *
1040 * *
1041 * Function *
1042 * -------- *
1043 * *
1044 * *
1045 * Returns *
1046 * ------- *
1047 * *
1048 ***************************************************************************/
1049 {
1050 DEBUG (("data_get_original_capacity() returns: %d\n",
1051 battery_data.capacity_max));
1052 return battery_data.capacity_max;
1053 }
1054
1055 /************************** Private Functions ***************************/
1056 #ifdef WIN32
1057 /* DWORD GetBatteryState */
1058 /**************************************************************************/
GetBatteryState(BATTERY_INFORMATION * bi)1059 DWORD GetBatteryState ( /* get information from the battery */
1060 BATTERY_INFORMATION *bi) /* out: the BATTERY_INFORMATION struct */
1061 /***************************************************************************
1062 * globals : *
1063 * called from : *
1064 * calls : *
1065 * *
1066 * Function *
1067 * -------- *
1068 * *
1069 * *
1070 * Returns *
1071 * ------- *
1072 * 0 .. if capacity values are relative; i.e. assume APM-style values *
1073 * 1 .. if capacity values are absolute; i.e. assume ACPI-style *
1074 ***************************************************************************/
1075 {
1076 DWORD dwResult;
1077 HDEVINFO hdev;
1078 unsigned int i;
1079
1080 dwResult = 0;
1081 if (!apm) /* --apm was given at cmdline => pretend ext. info not avail */
1082 {
1083 // IOCTL_BATTERY_QUERY_INFORMATION,
1084 // enumerate the batteries and ask each one for information.
1085 hdev = SetupDiGetClassDevs(
1086 &GUID_DEVICE_BATTERY, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
1087 if (INVALID_HANDLE_VALUE != hdev)
1088 {
1089 SP_DEVICE_INTERFACE_DATA did = {0};
1090 did.cbSize = sizeof(did);
1091
1092 for (i=0; i<10; i++)
1093 {
1094 if (SetupDiEnumDeviceInterfaces(hdev,0,&GUID_DEVICE_BATTERY,i,&did))
1095 {
1096 DWORD cbRequired = 0;
1097
1098 SetupDiGetDeviceInterfaceDetail(hdev, &did, 0, 0, &cbRequired, 0);
1099 if (ERROR_INSUFFICIENT_BUFFER == GetLastError())
1100 {
1101 PSP_DEVICE_INTERFACE_DETAIL_DATA pdidd =
1102 (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LPTR, cbRequired);
1103 if (pdidd)
1104 {
1105 pdidd->cbSize = sizeof(*pdidd);
1106 if (SetupDiGetDeviceInterfaceDetail(
1107 hdev, &did, pdidd, cbRequired, &cbRequired, 0))
1108 {
1109 // Enumerated a battery. Ask it for information.
1110 HANDLE hBattery = CreateFile(pdidd->DevicePath,
1111 GENERIC_READ | GENERIC_WRITE,
1112 FILE_SHARE_READ | FILE_SHARE_WRITE,
1113 NULL,
1114 OPEN_EXISTING,
1115 FILE_ATTRIBUTE_NORMAL,
1116 NULL);
1117 if (INVALID_HANDLE_VALUE != hBattery)
1118 {
1119 // Ask the battery for its tag.
1120 BATTERY_QUERY_INFORMATION bqi = {0};
1121 DWORD dwWait;
1122 DWORD dwOut;
1123 DWORD res;
1124
1125 dwWait = 100;
1126 res = DeviceIoControl(hBattery,
1127 IOCTL_BATTERY_QUERY_TAG,
1128 &dwWait,
1129 sizeof(dwWait),
1130 &bqi.BatteryTag,
1131 sizeof(bqi.BatteryTag),
1132 &dwOut,
1133 NULL);
1134 if (res && bqi.BatteryTag)
1135 {
1136 // With the tag, you can query the battery info.
1137 bqi.InformationLevel = BatteryInformation;
1138
1139 res = DeviceIoControl(hBattery,
1140 IOCTL_BATTERY_QUERY_INFORMATION,
1141 &bqi,
1142 sizeof(bqi),
1143 bi,
1144 sizeof(*bi),
1145 &dwOut,
1146 NULL);
1147 if (res)
1148 {
1149 if (!(bi->Capabilities & BATTERY_CAPACITY_RELATIVE))
1150 dwResult = 1;
1151 }
1152 }
1153 CloseHandle(hBattery);
1154 }
1155 }
1156 LocalFree(pdidd);
1157 }
1158 }
1159 }
1160 }
1161 SetupDiDestroyDeviceInfoList(hdev);
1162 }
1163 }
1164 return dwResult;
1165 }
1166
print_sps(SYSTEM_POWER_STATUS * sps)1167 void print_sps (SYSTEM_POWER_STATUS *sps)
1168 {
1169 printf ("SYSTEM_POWER_STATUS:\n");
1170 printf (" ACLineStatus = %d\n", sps->ACLineStatus);
1171 printf (" BatteryFlag = %x\n", sps->BatteryFlag);
1172 printf (" BatteryLifePercent = %d\n", sps->BatteryLifePercent);
1173 printf (" Reserved1 = %x\n", sps->Reserved1);
1174 printf (" BatteryLifeTime = %ld\n", sps->BatteryLifeTime);
1175 printf (" BatteryFullLifeTime = %ld\n", sps->BatteryFullLifeTime);
1176 }
1177
print_batinfo(BATTERY_INFORMATION * bi)1178 void print_batinfo (BATTERY_INFORMATION *bi)
1179 {
1180 printf ("BATTERY_INFORMATION:\n");
1181 printf (" Capabilities=0x%08lx\n", bi->Capabilities);
1182 printf (" Technology=%d\n", bi->Technology);
1183 printf (" Reserved=%02x %02x %02x\n", bi->Reserved[0], bi->Reserved[1], bi->Reserved[2]);
1184 printf (" Chemistry=%c%c%c%c\n", bi->Chemistry[0], bi->Chemistry[1], bi->Chemistry[2], bi->Chemistry[3]);
1185 printf (" DesignedCapacity=%ld\n", bi->DesignedCapacity);
1186 printf (" FullChargedCapacity=%ld\n", bi->FullChargedCapacity);
1187 printf (" DefaultAlert1=%ld\n", bi->DefaultAlert1);
1188 printf (" DefaultAlert2=%ld\n", bi->DefaultAlert2);
1189 printf (" CriticalBias=%ld\n", bi->CriticalBias);
1190 printf (" CycleCount=%ld\n", bi->CycleCount);
1191 }
1192 #endif
1193
print_battery_data(struct BATTERY_DATA * bd)1194 void print_battery_data(struct BATTERY_DATA *bd)
1195 {
1196 printf ("BATTERY_DATA:\n");
1197 printf (" charge_cur=%d\n", bd->charge_cur);
1198 printf (" charge_max=%d\n", bd->charge_max);
1199 printf (" capacity_cur=%d\n", bd->capacity_cur);
1200 printf (" capacity_max=%d\n", bd->capacity_max);
1201 printf (" cycles=%d\n", bd->cycles);
1202 printf (" charger_connected=%d\n", bd->charger_connected);
1203 printf (" is_charging=%d\n", bd->is_charging);
1204 printf (" custom_cap=%d\n", bd->custom_cap);
1205 printf (" custom_cap_value=%d\n", bd->custom_cap_value);
1206 printf (" extended_info=%d\n", bd->extended_info);
1207 printf (" unit=%s\n", bd->unit);
1208 }
1209