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