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, ¤tval);
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, ¤tval);
206 time_left = currentval * 60;
207 }
208 else
209 {
210 values = CFDictionaryGetValue(device_dict, CFSTR(kIOPSTimeToFullChargeKey));
211 CFNumberGetValue(values, kCFNumberSInt32Type, ¤tval);
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