1 #include "batman.h"
2 
3 #if defined(HAVE_CFBASE_H)
4 # include <CFBase.h>
5 # include <CFNumber.h>
6 # include <CFArray.h>
7 # include <CFDictionary.h>
8 # include <CFRunLoop.h>
9 # include <ps/IOPSKeys.h>
10 # include <ps/IOPowerSources.h>
11 #endif
12 
13 /* supported battery system schemes - irrespective of OS */
14 #define CHECK_NONE                   0
15 #define CHECK_ACPI                   1
16 #define CHECK_APM                    2
17 #define CHECK_PMU                    3
18 #define CHECK_SYS_CLASS_POWER_SUPPLY 4
19 
20 #define SYS_PWR
21 
22 static Ecore_Poller *poller = NULL;
23 
24 static int mode = CHECK_NONE;
25 
26 static int time_left = -2;
27 static int battery_full = -2;
28 static int have_battery = -2;
29 static int have_power = -2;
30 
31 static const char *sys_power_dir = "/sys/class/power_supply";
32 static Eina_Bool _batman_fallback_poll_cb(void *data EINA_UNUSED);
33 
34 static int
int_file_get(const char * file)35 int_file_get(const char *file)
36 {
37    int val = -1;
38    FILE *f = fopen(file, "r");
39    if (f)
40      {
41         char buf[256];
42         char *str = fgets(buf, sizeof(buf), f);
43         if (str) val = atoi(str);
44         fclose(f);
45      }
46    return val;
47 }
48 
49 static char *
str_file_get(const char * file)50 str_file_get(const char *file)
51 {
52    char *val = NULL;
53    FILE *f = fopen(file, "r");
54    if (f)
55      {
56         char buf[4096];
57         char *str = fgets(buf, sizeof(buf), f);
58         if (str)
59           {
60              size_t len = strlen(str);
61              if ((len > 0) && (str[len - 1] == '\n'))
62                {
63                   len--;
64                   str[len] = 0;
65                }
66              val = malloc(len + 1);
67              if (val) memcpy(val, str, len + 1);
68           }
69         fclose(f);
70      }
71    return val;
72 }
73 
74 static int
int_get(const char * buf)75 int_get(const char *buf)
76 {
77    const char *p = strchr(buf, ':');
78    if (!p) return 0;
79    p++;
80    while (*p == ' ')
81      p++;
82    return atoi(p);
83 }
84 
85 static char *
str_get(const char * buf)86 str_get(const char *buf)
87 {
88    const char *p = strchr(buf, ':');
89    const char *q;
90    char *ret;
91 
92    if (!p) return NULL;
93    p++;
94    while (*p == ' ')
95      p++;
96 
97    q = p + strlen(p) - 1;
98    while ((q > p) && ((*q == ' ') || (*q == '\n')))
99      q--;
100 
101    if (q < p) return NULL;
102    q++;
103    ret = malloc(q - p + 1);
104    if (!ret) return NULL;
105    memcpy(ret, p, q - p);
106    ret[q - p] = '\0';
107    return ret;
108 }
109 
110 static char *
file_str_entry_get(FILE * f,const char * entry)111 file_str_entry_get(FILE *f,
112                    const char *entry)
113 {
114    char buf[4096];
115    char *tmp;
116 
117    tmp = fgets(buf, sizeof(buf), f);
118    if (!tmp)
119      {
120         EINA_LOG_ERR("unexpected end of file, expected: '%s'", entry);
121         return NULL;
122      }
123    if (strcmp(tmp, entry) != 0)
124      {
125         EINA_LOG_ERR("unexpected file entry, expected: '%s'", entry);
126         return NULL;
127      }
128    tmp = str_get(tmp);
129    if (!tmp)
130      {
131         EINA_LOG_ERR("unexpected file entry, missing value for '%s'", entry);
132         return NULL;
133      }
134    return tmp;
135 }
136 
137 #if defined(HAVE_CFBASE_H) /* OS X */
138 /***---***/
139 static void darwin_init(void);
140 static void darwin_check(void);
141 
142 static void
darwin_init(void)143 darwin_init(void)
144 {
145    /* nothing to do */
146 }
147 
148 static void
darwin_check(void)149 darwin_check(void)
150 {
151    const void *values;
152    int device_num, device_count;
153    int currentval = 0, maxval = 0;
154    CFTypeRef blob;
155    CFArrayRef sources;
156    CFDictionaryRef device_dict;
157 
158    time_left = -1;
159    battery_full = -1;
160    have_battery = 0;
161    have_power = 0;
162 
163    /* Retrieve the power source data and the array of sources. */
164    blob = IOPSCopyPowerSourcesInfo();
165    sources = IOPSCopyPowerSourcesList(blob);
166    device_count = CFArrayGetCount(sources);
167    for (device_num = 0; device_num < device_count; device_num++)
168      {
169         CFTypeRef ps;
170 
171         /* Retrieve a dictionary of values for this device and the count of keys in the dictionary. */
172         ps = CFArrayGetValueAtIndex(sources, device_num);
173         device_dict = IOPSGetPowerSourceDescription(blob, ps);
174         /* Retrieve the charging key and save the present charging value if one exists. */
175         if (CFDictionaryGetValueIfPresent(device_dict,
176                                           CFSTR(kIOPSIsChargingKey), &values))
177           {
178              have_battery = 1;
179              if (CFBooleanGetValue(values) > 0) have_power = 1;
180              break;
181           }
182      }
183 
184    if (!have_battery)
185      {
186         CFRelease(sources);
187         CFRelease(blob);
188         have_power = 1;
189         return;
190      }
191 
192    /* Retrieve the current capacity key. */
193    values = CFDictionaryGetValue(device_dict, CFSTR(kIOPSCurrentCapacityKey));
194    CFNumberGetValue(values, kCFNumberSInt32Type, &currentval);
195    /* Retrieve the max capacity key. */
196    values = CFDictionaryGetValue(device_dict, CFSTR(kIOPSMaxCapacityKey));
197    CFNumberGetValue(values, kCFNumberSInt32Type, &maxval);
198    /* Calculate the percentage charged. */
199    battery_full = (currentval * 100) / maxval;
200 
201    /* Retrieve the remaining battery power or time until charged in minutes. */
202    if (!have_power)
203      {
204         values = CFDictionaryGetValue(device_dict, CFSTR(kIOPSTimeToEmptyKey));
205         CFNumberGetValue(values, kCFNumberSInt32Type, &currentval);
206         time_left = currentval * 60;
207      }
208    else
209      {
210         values = CFDictionaryGetValue(device_dict, CFSTR(kIOPSTimeToFullChargeKey));
211         CFNumberGetValue(values, kCFNumberSInt32Type, &currentval);
212         time_left = currentval * 60;
213      }
214    CFRelease(sources);
215    CFRelease(blob);
216 }
217 
218 #else
219 
220 /***---***/
221 /* new linux power class api to get power info - brand new and this code
222  * may have bugs, but it is a good attempt to get it right */
223 #if 0
224 static Eina_Bool linux_sys_class_power_supply_cb_event_fd_active(void *data,
225                                                                  Ecore_Fd_Handler *fd_handler);
226 static void      linux_sys_class_power_supply_check(void);
227 #endif
228 static void      linux_sys_class_power_supply_init(void);
229 
230 typedef struct _Sys_Class_Power_Supply_Uevent Sys_Class_Power_Supply_Uevent;
231 
232 #define BASIS_CHARGE  1
233 #define BASIS_ENERGY  2
234 #define BASIS_VOLTAGE 3
235 
236 struct _Sys_Class_Power_Supply_Uevent
237 {
238    char             *name;
239    int               fd;
240    Ecore_Fd_Handler *fd_handler;
241 
242    int               present;
243 
244    int               basis;
245    int               basis_empty;
246    int               basis_full;
247 
248    unsigned char     have_current_avg E_BITFIELD;
249    unsigned char     have_current_now E_BITFIELD;
250 };
251 
252 static Eina_List *events = NULL;
253 
254 #if 0
255 static Ecore_Timer *sys_class_delay_check = NULL;
256 
257 static Eina_Bool
258 linux_sys_class_power_supply_cb_delay_check(void *data)
259 {
260    linux_sys_class_power_supply_init();
261    _batman_fallback_poll_cb(NULL);
262    sys_class_delay_check = NULL;
263    return ECORE_CALLBACK_CANCEL;
264 }
265 
266 static Ecore_Timer *re_init_timer = NULL;
267 
268 static Eina_Bool
269 linux_sys_class_power_supply_cb_re_init(void *data)
270 {
271    Sys_Class_Power_Supply_Uevent *sysev;
272 
273    if (events)
274      {
275         EINA_LIST_FREE(events, sysev)
276           {
277 //           if (sysev->fd_handler)
278 //             ecore_main_fd_handler_del(sysev->fd_handler);
279 //           if (sysev->fd >= 0) close(sysev->fd);
280              E_FREE(sysev->name);
281              E_FREE(sysev);
282           }
283      }
284    linux_sys_class_power_supply_init();
285    re_init_timer = NULL;
286    return ECORE_CALLBACK_CANCEL;
287 }
288 
289 static Eina_Bool
290 linux_sys_class_power_supply_cb_event_fd_active(void *data,
291                                                 Ecore_Fd_Handler *fd_handler)
292 {
293    Sys_Class_Power_Supply_Uevent *sysev;
294 
295    sysev = data;
296    if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_READ))
297      {
298         int lost = 0;
299         for (;; )
300           {
301              char buf[1024];
302              int num;
303 
304              if ((num = read(sysev->fd, buf, sizeof(buf))) < 1)
305                {
306                   lost = ((errno == EIO) ||
307                           (errno == EBADF) ||
308                           (errno == EPIPE) ||
309                           (errno == EINVAL) ||
310                           (errno == ENOSPC) ||
311                           (errno == ENODEV));
312                   if (num <= 0) break;
313                }
314           }
315         if (lost)
316           {
317              events = eina_list_remove(events, sysev);
318 
319 //           if (sysev->fd_handler)
320 //             ecore_main_fd_handler_del(sysev->fd_handler);
321 //           if (sysev->fd >= 0) close(sysev->fd);
322              E_FREE(sysev->name);
323              E_FREE(sysev);
324 
325              if (re_init_timer) ecore_timer_del(re_init_timer);
326              re_init_timer = ecore_timer_loop_add(1.0, linux_sys_class_power_supply_cb_re_init, NULL);
327           }
328         else
329           {
330              if (sys_class_delay_check) ecore_timer_del(sys_class_delay_check);
331              sys_class_delay_check = ecore_timer_loop_add(0.2, linux_sys_class_power_supply_cb_delay_check, NULL);
332           }
333      }
334    return ECORE_CALLBACK_CANCEL;
335 }
336 
337 #endif
338 static void
linux_sys_class_power_supply_sysev_init(Sys_Class_Power_Supply_Uevent * sysev)339 linux_sys_class_power_supply_sysev_init(Sys_Class_Power_Supply_Uevent *sysev)
340 {
341    char buf[4096];
342    const char *dir = sys_power_dir;
343 
344    sysev->basis = 0;
345    sysev->have_current_avg = 0;
346    sysev->have_current_now = 0;
347    snprintf(buf, sizeof(buf), "%s/%s/present", dir, sysev->name);
348    sysev->present = int_file_get(buf);
349    if (!sysev->present) return;
350    snprintf(buf, sizeof(buf), "%s/%s/current_avg", dir, sysev->name);
351    if (ecore_file_exists(buf)) sysev->have_current_avg = 1;
352    snprintf(buf, sizeof(buf), "%s/%s/current_now", dir, sysev->name);
353    if (ecore_file_exists(buf)) sysev->have_current_now = 1;
354 
355    snprintf(buf, sizeof(buf), "%s/%s/voltage_max", dir, sysev->name);
356    if (ecore_file_exists(buf)) sysev->basis = BASIS_VOLTAGE;
357    snprintf(buf, sizeof(buf), "%s/%s/voltage_max_design", dir, sysev->name);
358    if (ecore_file_exists(buf)) sysev->basis = BASIS_VOLTAGE;
359 
360    snprintf(buf, sizeof(buf), "%s/%s/energy_full", dir, sysev->name);
361    if (ecore_file_exists(buf)) sysev->basis = BASIS_ENERGY;
362    snprintf(buf, sizeof(buf), "%s/%s/energy_full_design", dir, sysev->name);
363    if (ecore_file_exists(buf)) sysev->basis = BASIS_ENERGY;
364 
365    snprintf(buf, sizeof(buf), "%s/%s/charge_full", dir, sysev->name);
366    if (ecore_file_exists(buf)) sysev->basis = BASIS_CHARGE;
367    snprintf(buf, sizeof(buf), "%s/%s/charge_full_design", dir, sysev->name);
368    if (ecore_file_exists(buf)) sysev->basis = BASIS_CHARGE;
369 
370    if (sysev->basis == BASIS_CHARGE)
371      {
372         snprintf(buf, sizeof(buf), "%s/%s/charge_full", dir, sysev->name);
373         sysev->basis_full = int_file_get(buf);
374         snprintf(buf, sizeof(buf), "%s/%s/charge_empty", dir, sysev->name);
375         sysev->basis_empty = int_file_get(buf);
376         if (sysev->basis_full < 0)
377           {
378              snprintf(buf, sizeof(buf), "%s/%s/charge_full_design", dir, sysev->name);
379              sysev->basis_full = int_file_get(buf);
380           }
381         if (sysev->basis_empty < 0)
382           {
383              snprintf(buf, sizeof(buf), "%s/%s/charge_empty_design", dir, sysev->name);
384              sysev->basis_empty = int_file_get(buf);
385           }
386      }
387    else if (sysev->basis == BASIS_ENERGY)
388      {
389         snprintf(buf, sizeof(buf), "%s/%s/energy_full", dir, sysev->name);
390         sysev->basis_full = int_file_get(buf);
391         snprintf(buf, sizeof(buf), "%s/%s/energy_empty", dir, sysev->name);
392         sysev->basis_empty = int_file_get(buf);
393         if (sysev->basis_full < 0)
394           {
395              snprintf(buf, sizeof(buf), "%s/%s/energy_full_design", dir, sysev->name);
396              sysev->basis_full = int_file_get(buf);
397           }
398         if (sysev->basis_empty < 0)
399           {
400              snprintf(buf, sizeof(buf), "%s/%s/energy_empty_design", dir, sysev->name);
401              sysev->basis_empty = int_file_get(buf);
402           }
403      }
404    else if (sysev->basis == BASIS_VOLTAGE)
405      {
406         snprintf(buf, sizeof(buf), "%s/%s/voltage_max", dir, sysev->name);
407         sysev->basis_full = int_file_get(buf);
408         snprintf(buf, sizeof(buf), "%s/%s/voltage_min", dir, sysev->name);
409         sysev->basis_empty = int_file_get(buf);
410         if (sysev->basis_full < 0)
411           {
412              snprintf(buf, sizeof(buf), "%s/%s/voltage_max_design", dir, sysev->name);
413              sysev->basis_full = int_file_get(buf);
414           }
415         if (sysev->basis_empty < 0)
416           {
417              snprintf(buf, sizeof(buf), "%s/%s/voltage_min_design", dir, sysev->name);
418              sysev->basis_empty = int_file_get(buf);
419           }
420      }
421 }
422 
423 static int
linux_sys_class_power_supply_is_battery(char * name)424 linux_sys_class_power_supply_is_battery(char *name)
425 {
426    int fd;
427    int ret = 0;
428    char buf[256];
429    const char *dir = sys_power_dir;
430 
431    snprintf(buf, sizeof(buf), "%s/%s/type", dir, name);
432    fd = open(buf, O_RDONLY);
433    if (fd < 0)
434      {
435         ret = 0;
436         goto NO_OPEN;
437      }
438    else if (read(fd, buf, sizeof(buf)) < 1)
439      ret = 0;
440    else if (!strncmp(buf, "Battery", 7))
441      ret = 1;
442 
443    close(fd);
444 
445 NO_OPEN:
446    return ret;
447 }
448 
449 static void
linux_sys_class_power_supply_init(void)450 linux_sys_class_power_supply_init(void)
451 {
452    Eina_List *l;
453 
454    if (events)
455      {
456         Sys_Class_Power_Supply_Uevent *sysev;
457 
458         EINA_LIST_FOREACH(events, l, sysev)
459           linux_sys_class_power_supply_sysev_init(sysev);
460      }
461    else
462      {
463         Eina_List *bats;
464         char *name;
465 //      char buf[4096];
466 
467         bats = ecore_file_ls("/sys/class/power_supply/");
468 //        bats = ecore_file_ls("./TST");
469         if (bats)
470           {
471              events = NULL;
472 
473              EINA_LIST_FREE(bats, name)
474                {
475                   Sys_Class_Power_Supply_Uevent *sysev;
476 
477                   if (!(linux_sys_class_power_supply_is_battery(name)))
478                     {
479                        E_FREE(name);
480                        continue;
481                     }
482                   sysev = (Sys_Class_Power_Supply_Uevent *)calloc(1, sizeof(Sys_Class_Power_Supply_Uevent));
483                   sysev->name = name;
484 //                snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/uevent", name);
485 //                sysev->fd = open(buf, O_RDONLY);
486 //                if (sysev->fd >= 0)
487 //                  sysev->fd_handler = ecore_main_fd_handler_add(sysev->fd,
488 //                                                                ECORE_FD_READ,
489 //                                                                linux_sys_class_power_supply_cb_event_fd_active,
490 //                                                                sysev,
491 //                                                                NULL, NULL);
492 //
493                   events = eina_list_append(events, sysev);
494                   linux_sys_class_power_supply_sysev_init(sysev);
495                }
496           }
497      }
498 }
499 
500 static void
linux_sys_class_power_supply_check(void)501 linux_sys_class_power_supply_check(void)
502 {
503    Eina_List *l;
504    char *name;
505    char buf[4096];
506    const char *dir = sys_power_dir;
507 
508    battery_full = -1;
509    time_left = -1;
510    have_battery = 0;
511    have_power = 0;
512 
513    if (events)
514      {
515         Sys_Class_Power_Supply_Uevent *sysev;
516         int total_pwr_now;
517         int total_pwr_max;
518         int nofull = 0;
519 
520         total_pwr_now = 0;
521         total_pwr_max = 0;
522         time_left = 0;
523         EINA_LIST_FOREACH(events, l, sysev)
524           {
525              char *tmp;
526              int present = 0;
527              int charging = -1;
528              int capacity = -1;
529              int current = -1;
530              int time_to_full = -1;
531              int time_to_empty = -1;
532              int full = -1;
533              int pwr_now = -1;
534              int pwr_empty = -1;
535              int pwr_full = -1;
536              int pwr = 0;
537 
538              name = sysev->name;
539 
540              /* fetch more generic info */
541              // init
542              present = sysev->present;
543              if (!present) continue;
544              snprintf(buf, sizeof(buf), "%s/%s/capacity", dir, name);
545              capacity = int_file_get(buf);
546              if (sysev->have_current_avg)
547                {
548                   snprintf(buf, sizeof(buf), "%s/%s/current_avg", dir, name);
549                   current = int_file_get(buf);
550                }
551              else if (sysev->have_current_now)
552                {
553                   snprintf(buf, sizeof(buf), "%s/%s/current_now", dir, name);
554                   current = int_file_get(buf);
555                }
556 
557              /* FIXME: do we get a uevent on going from charging to full?
558               * if so, move this to init */
559              snprintf(buf, sizeof(buf), "%s/%s/status", dir, name);
560              tmp = str_file_get(buf);
561              if (tmp)
562                {
563                   full = 0;
564                   if (!strncasecmp("discharging", tmp, 11)) charging = 0;
565                   else if (!strncasecmp("unknown", tmp, 7))
566                     charging = 0;
567                   else if (!strncasecmp("not charging", tmp, 12))
568                     charging = 0;
569                   else if (!strncasecmp("charging", tmp, 8))
570                     charging = 1;
571                   else if (!strncasecmp("full", tmp, 4))
572                     {
573                        full = 1;
574                        charging = 0;
575                     }
576                   E_FREE(tmp);
577                }
578              /* some batteries can/will/want to predict how long they will
579               * last. if so - take what the battery says. too bad if it's
580               * wrong. that's a buggy battery or driver */
581              if (!full)
582                {
583                   nofull++;
584                   if (charging)
585                     {
586                        snprintf(buf, sizeof(buf), "%s/%s/time_to_full_now", dir, name);
587                        time_to_full = int_file_get(buf);
588                     }
589                   else
590                     {
591                        snprintf(buf, sizeof(buf), "%s/%s/time_to_empty_now", dir, name);
592                        time_to_empty = int_file_get(buf);
593                     }
594                }
595 
596              /* now get charge, energy and voltage. take the one that provides
597               * the best info (charge first, then energy, then voltage */
598              if (sysev->basis == BASIS_CHARGE)
599                snprintf(buf, sizeof(buf), "%s/%s/charge_now", dir, name);
600              else if (sysev->basis == BASIS_ENERGY)
601                snprintf(buf, sizeof(buf), "%s/%s/energy_now", dir, name);
602              else if (sysev->basis == BASIS_VOLTAGE)
603                snprintf(buf, sizeof(buf), "%s/%s/voltage_now", dir, name);
604              pwr_now = int_file_get(buf);
605              pwr_empty = sysev->basis_empty;
606              pwr_full = sysev->basis_full;
607              if ((sysev->basis == BASIS_VOLTAGE) &&
608                  (capacity >= 0))
609                {
610                   /* if we use voltage as basis.. we're not very accurate
611                    * so we should prefer capacity readings */
612                   pwr_empty = -1;
613                   pwr_full = -1;
614                   pwr_now = -1;
615                }
616 
617              if (pwr_empty < 0) pwr_empty = 0;
618 
619              if ((pwr_full > 0) && (pwr_full > pwr_empty))
620                {
621                   if (full) pwr_now = pwr_full;
622                   else
623                     {
624                        if (pwr_now < 0)
625                          pwr_now = (((long long)capacity * ((long long)pwr_full - (long long)pwr_empty)) / 100) + pwr_empty;
626                     }
627 
628                   if (sysev->present) have_battery = 1;
629                   if (charging)
630                     {
631                        have_power = 1;
632                        if (time_to_full >= 0)
633                          {
634                             if (time_to_full > time_left)
635                               time_left = time_to_full;
636                          }
637                        else
638                          {
639                             if (current == 0) time_left = 0;
640                             else if (current < 0)
641                               time_left = -1;
642                             else
643                               {
644                                  pwr = (((long long)pwr_full - (long long)pwr_now) * 3600) / -current;
645                                  if (pwr > time_left) time_left = pwr;
646                               }
647                          }
648                     }
649                   else
650                     {
651                        have_power = 0;
652                        if (time_to_empty >= 0) time_left += time_to_empty;
653                        else
654                          {
655                             if (time_to_empty < 0)
656                               {
657                                  if (current > 0)
658                                    {
659                                       pwr = (((long long)pwr_now - (long long)pwr_empty) * 3600) / current;
660                                       time_left += pwr;
661                                    }
662                               }
663                          }
664                     }
665                   total_pwr_now += pwr_now - pwr_empty;
666                   total_pwr_max += pwr_full - pwr_empty;
667                }
668              /* simple current battery fallback */
669              else
670                {
671                   if (sysev->present) have_battery = 1;
672                   if (charging) have_power = 1;
673                   total_pwr_max = 100;
674                   total_pwr_now = capacity;
675                   if (total_pwr_now < 100) nofull = 1;
676                }
677           }
678         if (total_pwr_max > 0)
679           battery_full = ((long long)total_pwr_now * 100) / total_pwr_max;
680         if (nofull == 0)
681           time_left = -1;
682      }
683 }
684 
685 /***---***/
686 /* "here and now" ACPI based power checking. is there for linux and most
687  * modern laptops. as of linux 2.6.24 it is replaced with
688  * linux_sys_class_power_supply_init/check() though as this is the new
689  * power class api to poll for power stuff
690  */
691 static Eina_Bool linux_acpi_cb_acpid_add(void *data,
692                                          int type,
693                                          void *event);
694 static Eina_Bool linux_acpi_cb_acpid_del(void *data,
695                                          int type,
696                                          void *event);
697 static Eina_Bool linux_acpi_cb_acpid_data(void *data,
698                                           int type,
699                                           void *event);
700 static void      linux_acpi_init(void);
701 static void      linux_acpi_check(void);
702 
703 static int acpi_max_full = -1;
704 static int acpi_max_design = -1;
705 static Ecore_Con_Server *acpid = NULL;
706 static Ecore_Event_Handler *acpid_handler_add = NULL;
707 static Ecore_Event_Handler *acpid_handler_del = NULL;
708 static Ecore_Event_Handler *acpid_handler_data = NULL;
709 static Ecore_Timer *delay_check = NULL;
710 static int event_fd = -1;
711 static Ecore_Fd_Handler *event_fd_handler = NULL;
712 
713 static Eina_Bool
linux_acpi_cb_delay_check(void * data EINA_UNUSED)714 linux_acpi_cb_delay_check(void *data EINA_UNUSED)
715 {
716    linux_acpi_init();
717    _batman_fallback_poll_cb(NULL);
718    delay_check = NULL;
719    return ECORE_CALLBACK_CANCEL;
720 }
721 
722 static Eina_Bool
linux_acpi_cb_acpid_add(void * data EINA_UNUSED,int type EINA_UNUSED,void * event EINA_UNUSED)723 linux_acpi_cb_acpid_add(void *data EINA_UNUSED,
724                         int type EINA_UNUSED,
725                         void *event EINA_UNUSED)
726 {
727    return ECORE_CALLBACK_PASS_ON;
728 }
729 
730 static Eina_Bool
linux_acpi_cb_acpid_del(void * data EINA_UNUSED,int type EINA_UNUSED,void * event EINA_UNUSED)731 linux_acpi_cb_acpid_del(void *data EINA_UNUSED,
732                         int type EINA_UNUSED,
733                         void *event EINA_UNUSED)
734 {
735    ecore_con_server_del(acpid);
736    acpid = NULL;
737    if (acpid_handler_add) ecore_event_handler_del(acpid_handler_add);
738    acpid_handler_add = NULL;
739    if (acpid_handler_del) ecore_event_handler_del(acpid_handler_del);
740    acpid_handler_del = NULL;
741    if (acpid_handler_data) ecore_event_handler_del(acpid_handler_data);
742    acpid_handler_data = NULL;
743    return ECORE_CALLBACK_PASS_ON;
744 }
745 
746 static Eina_Bool
linux_acpi_cb_acpid_data(void * data EINA_UNUSED,int type EINA_UNUSED,void * event EINA_UNUSED)747 linux_acpi_cb_acpid_data(void *data EINA_UNUSED,
748                          int type EINA_UNUSED,
749                          void *event EINA_UNUSED)
750 {
751    if (delay_check) ecore_timer_del(delay_check);
752    delay_check = ecore_timer_loop_add(0.2, linux_acpi_cb_delay_check, NULL);
753    return ECORE_CALLBACK_PASS_ON;
754 }
755 
756 static Eina_Bool
linux_acpi_cb_event_fd_active(void * data EINA_UNUSED,Ecore_Fd_Handler * fd_handler)757 linux_acpi_cb_event_fd_active(void *data EINA_UNUSED,
758                               Ecore_Fd_Handler *fd_handler)
759 {
760    if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_READ))
761      {
762         int lost = 0;
763         for (;; )
764           {
765              char buf[1024];
766              int num;
767 
768              if ((num = read(event_fd, buf, sizeof(buf))) < 1)
769                {
770                   lost = ((errno == EIO) ||
771                           (errno == EBADF) ||
772                           (errno == EPIPE) ||
773                           (errno == EINVAL) ||
774                           (errno == ENOSPC));
775                   if (num == 0) break;
776                }
777           }
778         if (lost)
779           {
780              ecore_main_fd_handler_del(event_fd_handler);
781              event_fd_handler = NULL;
782              close(event_fd);
783              event_fd = -1;
784           }
785         else
786           {
787              if (delay_check) ecore_timer_del(delay_check);
788              delay_check = ecore_timer_loop_add(0.2, linux_acpi_cb_delay_check, NULL);
789           }
790      }
791    return ECORE_CALLBACK_RENEW;
792 }
793 
794 static void
linux_acpi_init(void)795 linux_acpi_init(void)
796 {
797    Eina_Iterator *powers;
798    Eina_Iterator *bats;
799 
800    bats = eina_file_direct_ls("/proc/acpi/battery");
801    if (bats)
802      {
803         Eina_File_Direct_Info *info;
804         FILE *f;
805         char *tmp;
806         char buf[(PATH_MAX * 2) + 128];
807 
808         have_power = 0;
809         powers = eina_file_direct_ls("/proc/acpi/ac_adapter");
810         if (powers)
811           {
812              EINA_ITERATOR_FOREACH(powers, info)
813                {
814                   if (info->name_length + sizeof("/state") >= sizeof(buf)) continue;
815                   snprintf(buf, sizeof(buf), "%s/state", info->path);
816                   f = fopen(buf, "r");
817                   if (f)
818                     {
819                        /* state */
820                        tmp = fgets(buf, sizeof(buf), f);
821                        if (tmp) tmp = str_get(tmp);
822                        if (tmp)
823                          {
824                             if (!strcmp(tmp, "on-line")) have_power = 1;
825                             E_FREE(tmp);
826                          }
827                        fclose(f);
828                     }
829                }
830              eina_iterator_free(powers);
831           }
832 
833         have_battery = 0;
834         acpi_max_full = 0;
835         acpi_max_design = 0;
836         EINA_ITERATOR_FOREACH(bats, info)
837           {
838              snprintf(buf, sizeof(buf), "%s/info", info->path);
839              f = fopen(buf, "r");
840              if (f)
841                {
842                   /* present */
843                   tmp = fgets(buf, sizeof(buf), f);
844                   if (tmp) tmp = str_get(tmp);
845                   if (tmp)
846                     {
847                        if (!strcmp(tmp, "yes")) have_battery = 1;
848                        E_FREE(tmp);
849                     }
850                   /* design cap */
851                   tmp = fgets(buf, sizeof(buf), f);
852                   if (tmp) tmp = str_get(tmp);
853                   if (tmp)
854                     {
855                        if (strcmp(tmp, "unknown")) acpi_max_design += atoi(tmp);
856                        E_FREE(tmp);
857                     }
858                   /* last full cap */
859                   tmp = fgets(buf, sizeof(buf), f);
860                   if (tmp) tmp = str_get(tmp);
861                   if (tmp)
862                     {
863                        if (strcmp(tmp, "unknown")) acpi_max_full += atoi(tmp);
864                        E_FREE(tmp);
865                     }
866                   fclose(f);
867                }
868           }
869 
870         eina_iterator_free(bats);
871      }
872    if (!acpid)
873      {
874         acpid = ecore_con_server_connect(ECORE_CON_LOCAL_SYSTEM,
875                                          "/var/run/acpid.socket", -1, NULL);
876         if (acpid)
877           {
878              acpid_handler_add = ecore_event_handler_add(ECORE_CON_EVENT_SERVER_ADD,
879                                                          linux_acpi_cb_acpid_add, NULL);
880              acpid_handler_del = ecore_event_handler_add(ECORE_CON_EVENT_SERVER_DEL,
881                                                          linux_acpi_cb_acpid_del, NULL);
882              acpid_handler_data = ecore_event_handler_add(ECORE_CON_EVENT_SERVER_DATA,
883                                                           linux_acpi_cb_acpid_data, NULL);
884           }
885         else
886           {
887              if (event_fd < 0)
888                {
889                   event_fd = open("/proc/acpi/event", O_RDONLY);
890                   if (event_fd >= 0)
891                     event_fd_handler = ecore_main_fd_handler_add(event_fd,
892                                                                  ECORE_FD_READ,
893                                                                  linux_acpi_cb_event_fd_active,
894                                                                  NULL,
895                                                                  NULL, NULL);
896                }
897           }
898      }
899 }
900 
901 static void
linux_acpi_check(void)902 linux_acpi_check(void)
903 {
904    Eina_List *bats;
905 
906    battery_full = -1;
907    time_left = -1;
908    have_battery = 0;
909    have_power = 0;
910 
911    bats = ecore_file_ls("/proc/acpi/battery");
912    if (bats)
913      {
914         char *name;
915         int rate = 0;
916         int capacity = 0;
917 
918         EINA_LIST_FREE(bats, name)
919           {
920              char buf[4096];
921              char *tmp;
922              FILE *f;
923 
924              snprintf(buf, sizeof(buf), "/proc/acpi/battery/%s/state", name);
925              E_FREE(name);
926              f = fopen(buf, "r");
927              if (!f) continue;
928 
929              tmp = file_str_entry_get(f, "present:");
930              if (!tmp) goto fclose_and_continue;
931              if (!strcasecmp(tmp, "yes")) have_battery = 1;
932              E_FREE(tmp);
933 
934              tmp = file_str_entry_get(f, "capacity state:");
935              if (!tmp) goto fclose_and_continue;
936              E_FREE(tmp);
937 
938              tmp = file_str_entry_get(f, "charging state:");
939              if (!tmp) goto fclose_and_continue;
940              if ((have_power == 0) && (!strcasecmp(tmp, "charging")))
941                have_power = 1;
942              E_FREE(tmp);
943 
944              tmp = file_str_entry_get(f, "present rate:");
945              if (!tmp) goto fclose_and_continue;
946              if (strcasecmp(tmp, "unknown")) rate += atoi(tmp);
947              E_FREE(tmp);
948 
949              tmp = file_str_entry_get(f, "remaining capacity:");
950              if (!tmp) goto fclose_and_continue;
951              if (strcasecmp(tmp, "unknown")) capacity += atoi(tmp);
952              E_FREE(tmp);
953 
954 fclose_and_continue:
955              fclose(f);
956           }
957 
958         if (acpi_max_full > 0)
959           battery_full = 100 * (long long)capacity / acpi_max_full;
960         else if (acpi_max_design > 0)
961           battery_full = 100 * (long long)capacity / acpi_max_design;
962         else
963           battery_full = -1;
964         if (rate <= 0) time_left = -1;
965         else
966           {
967              if (have_power)
968                time_left = (3600 * ((long long)acpi_max_full - (long long)capacity)) / rate;
969              else
970                time_left = (3600 * (long long)capacity) / rate;
971           }
972      }
973 }
974 
975 /* old school apm support - very old laptops and some devices support this.
976  * this is here for legacy support and i wouldn't suggest spending any
977  * effort on it as it is complete below as best i know, but could have missed
978  * one or 2 things, but not worth fixing */
979 static void linux_apm_init(void);
980 static void linux_apm_check(void);
981 
982 static void
linux_apm_init(void)983 linux_apm_init(void)
984 {
985    /* nothing to do */
986 }
987 
988 static void
linux_apm_check(void)989 linux_apm_check(void)
990 {
991    FILE *f;
992    char s1[32], s2[32], s3[32], *endptr;
993    int apm_flags, ac_stat, bat_stat, bat_flags, bat_val, time_val;
994 
995    battery_full = -1;
996    time_left = -1;
997    have_battery = 0;
998    have_power = 0;
999 
1000    f = fopen("/proc/apm", "r");
1001    if (!f) return;
1002 
1003    if (fscanf(f, "%*s %*s %x %x %x %x %31s %31s %31s",
1004               &apm_flags, &ac_stat, &bat_stat, &bat_flags, s1, s2, s3) != 7)
1005      {
1006         fclose(f);
1007         return;
1008      }
1009    fclose(f);
1010 
1011    bat_val = strtol(s1, &endptr, 10);
1012    if (*endptr != '%') return;
1013 
1014    else if (!strcmp(s3, "min"))
1015      time_val = atoi(s2) * 60;
1016    else time_val = 0;
1017 
1018    if ((bat_flags != 0xff) && (bat_flags & 0x80))
1019      {
1020         have_battery = 0;
1021         have_power = 0;
1022         battery_full = 100;
1023         time_left = 0;
1024         return;
1025      }
1026 
1027    if (bat_val >= 0)
1028      {
1029         have_battery = 1;
1030         have_power = ac_stat;
1031         battery_full = bat_val;
1032         if (battery_full > 100) battery_full = 100;
1033         if (ac_stat == 1) time_left = -1;
1034         else time_left = time_val;
1035      }
1036    else
1037      {
1038         switch (bat_stat)
1039           {
1040            case 0: /* high */
1041              have_battery = 1;
1042              have_power = ac_stat;
1043              battery_full = 100;
1044              time_left = -1;
1045              break;
1046 
1047            case 1: /* medium */
1048              have_battery = 1;
1049              have_power = ac_stat;
1050              battery_full = 50;
1051              time_left = -1;
1052              break;
1053 
1054            case 2: /* low */
1055              have_battery = 1;
1056              have_power = ac_stat;
1057              battery_full = 25;
1058              time_left = -1;
1059              break;
1060 
1061            case 3: /* charging */
1062              have_battery = 1;
1063              have_power = ac_stat;
1064              battery_full = 100;
1065              time_left = -1;
1066              break;
1067           }
1068      }
1069 }
1070 
1071 /* for older mac powerbooks. legacy as well like linux_apm_init/check. leave
1072  * it alone unless you have to touch it */
1073 static void linux_pmu_init(void);
1074 static void linux_pmu_check(void);
1075 
1076 static void
linux_pmu_init(void)1077 linux_pmu_init(void)
1078 {
1079    /* nothing to do */
1080 }
1081 
1082 static void
linux_pmu_check(void)1083 linux_pmu_check(void)
1084 {
1085    FILE *f;
1086    char buf[4096];
1087    Eina_List *bats;
1088    char *name;
1089    int ac = 0;
1090    int charge = 0;
1091    int max_charge = 0;
1092    int seconds = 0;
1093    int curcharge = 0;
1094    int curmax = 0;
1095 
1096    f = fopen("/proc/pmu/info", "r");
1097    if (f)
1098      {
1099         char *tmp;
1100         /* Skip driver */
1101         tmp = fgets(buf, sizeof(buf), f);
1102         if (!tmp)
1103           {
1104              EINA_LOG_ERR("no driver info in /proc/pmu/info");
1105              goto fclose_and_continue;
1106           }
1107         /* Skip firmware */
1108         tmp = fgets(buf, sizeof(buf), f);
1109         if (!tmp)
1110           {
1111              EINA_LOG_ERR("no firmware info in /proc/pmu/info");
1112              goto fclose_and_continue;
1113           }
1114         /* Read ac */
1115         tmp = fgets(buf, sizeof(buf), f);
1116         if (!tmp)
1117           {
1118              EINA_LOG_ERR("no AC info in /proc/pmu/info");
1119              goto fclose_and_continue;
1120           }
1121         ac = int_get(buf);
1122 fclose_and_continue:
1123         fclose(f);
1124      }
1125    bats = ecore_file_ls("/proc/pmu");
1126    if (bats)
1127      {
1128         have_battery = 1;
1129         have_power = ac;
1130         EINA_LIST_FREE(bats, name)
1131           {
1132              if (strncmp(name, "battery", 7)) continue;
1133              snprintf(buf, sizeof(buf), "/proc/pmu/%s", name);
1134              f = fopen(buf, "r");
1135              if (f)
1136                {
1137                   int timeleft = 0;
1138                   int current = 0;
1139 
1140                   while (fgets(buf, sizeof (buf), f))
1141                     {
1142                        char *token;
1143 
1144                        if ((token = strtok(buf, ":")))
1145                          {
1146                             if (!strncmp("charge", token, 6))
1147                               charge = atoi(strtok(0, ": "));
1148                             else if (!strncmp("max_charge", token, 9))
1149                               max_charge = atoi(strtok(0, ": "));
1150                             else if (!strncmp("current", token, 7))
1151                               current = atoi(strtok(0, ": "));
1152                             else if (!strncmp("time rem", token, 8))
1153                               timeleft = atoi(strtok(0, ": "));
1154                             else
1155                               strtok(0, ": ");
1156                          }
1157                     }
1158                   curmax += max_charge;
1159                   curcharge += charge;
1160                   fclose(f);
1161                   if (!current)
1162                     {
1163                        /* Neither charging nor discharging */
1164                     }
1165                   else if (!ac)
1166                     {
1167                        /* When on dc, we are discharging */
1168                        seconds += timeleft;
1169                     }
1170                   else
1171                     {
1172                        /* Charging - works in parallel */
1173                        seconds = MAX(timeleft, seconds);
1174                     }
1175                }
1176 
1177              E_FREE(name);
1178           }
1179         if (max_charge > 0) battery_full = ((long long)charge * 100) / max_charge;
1180         else battery_full = 0;
1181         time_left = seconds;
1182      }
1183    else
1184      {
1185         have_power = ac;
1186         have_battery = 0;
1187         battery_full = -1;
1188         time_left = -1;
1189      }
1190 }
1191 
1192 #endif
1193 
1194 static Eina_Bool
_batman_fallback_poll_cb(void * data)1195 _batman_fallback_poll_cb(void *data)
1196 {
1197    Instance *inst = data;
1198 
1199 #if defined(HAVE_CFBASE_H) /* OS X */
1200    darwin_check();
1201    return ECORE_CALLBACK_RENEW;
1202 #else
1203    switch (mode)
1204      {
1205       case CHECK_ACPI:
1206         linux_acpi_check();
1207         break;
1208 
1209       case CHECK_APM:
1210         linux_apm_check();
1211         break;
1212 
1213       case CHECK_PMU:
1214         linux_pmu_check();
1215         break;
1216 
1217       case CHECK_SYS_CLASS_POWER_SUPPLY:
1218         linux_sys_class_power_supply_check();
1219         break;
1220 
1221       default:
1222         battery_full = -1;
1223         time_left = -1;
1224         have_battery = 0;
1225         have_power = 0;
1226         break;
1227      }
1228 #endif
1229 
1230    _batman_update(inst, battery_full, time_left, have_battery, have_power);
1231 
1232    return EINA_TRUE;
1233 }
1234 
1235 static int
dir_has_contents(const char * dir)1236 dir_has_contents(const char *dir)
1237 {
1238    Eina_List *bats;
1239    char *file;
1240    int count;
1241 
1242    bats = ecore_file_ls(dir);
1243 
1244    count = eina_list_count(bats);
1245    EINA_LIST_FREE(bats, file)
1246      E_FREE(file);
1247    if (count > 0) return 1;
1248    return 0;
1249 }
1250 
1251 int
_batman_fallback_start(Instance * inst)1252 _batman_fallback_start(Instance *inst)
1253 {
1254 #if defined(__OpenBSD__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__)
1255    return 0;
1256 #elif defined(HAVE_CFBASE_H) /* OS X */
1257    darwin_init();
1258 #else
1259    if ((ecore_file_is_dir(sys_power_dir)) && (dir_has_contents(sys_power_dir)))
1260      {
1261         mode = CHECK_SYS_CLASS_POWER_SUPPLY;
1262         linux_sys_class_power_supply_init();
1263      }
1264    else if (ecore_file_is_dir("/proc/acpi")) /* <= 2.6.24 */
1265      {
1266         mode = CHECK_ACPI;
1267         linux_acpi_init();
1268      }
1269    else if (ecore_file_exists("/proc/apm"))
1270      {
1271         mode = CHECK_APM;
1272         linux_apm_init();
1273      }
1274    else if (ecore_file_is_dir("/proc/pmu"))
1275      {
1276         mode = CHECK_PMU;
1277         linux_pmu_init();
1278      }
1279 #endif
1280 
1281    poller = ecore_poller_add(ECORE_POLLER_CORE, inst->cfg->batman.poll_interval, _batman_fallback_poll_cb, inst);
1282 
1283    return 1;
1284 }
1285 
1286 void
_batman_fallback_stop(void)1287 _batman_fallback_stop(void)
1288 {
1289    E_FREE_FUNC(poller, ecore_poller_del);
1290 }
1291 
1292