battery.c 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. #include <err.h>
  2. #include <fcntl.h>
  3. #include <stdio.h>
  4. #include <string.h>
  5. #include <limits.h>
  6. #include <stdint.h>
  7. #include <unistd.h>
  8. #include "battery.h"
  9. #include "../lib/util.h"
  10. #include "../aslstatus.h"
  11. #define MAX_STATE 13
  12. #define STATE_PATTERN "%" STR(MAX_STATE) "[^\n]s"
  13. #define SYS_CLASS "/sys/class/power_supply"
  14. static uint8_t pick(const char *bat, int *fd, const char **arr, size_t len);
  15. static uint8_t get_state(char state[MAX_STATE], int *fd, const char *bat);
  16. static void battery_remaining_cleanup(void *ptr);
  17. void
  18. battery_perc(char *out,
  19. const char *bat,
  20. uint32_t __unused _i,
  21. static_data_t *static_data)
  22. {
  23. size_t readed;
  24. char perc[4]; /* len(str(100)) + 1 */
  25. int *fd = static_data->data;
  26. if (!static_data->cleanup) static_data->cleanup = fd_cleanup;
  27. if (!sysfs_fd_or_rewind(fd, SYS_CLASS, bat, "capacity")) ERRRET(out);
  28. if (!eread_ret(readed, *fd, WITH_LEN(perc))) ERRRET(out);
  29. perc[--readed /* '\n' at the end */] = '\0';
  30. bprintf(out, "%.3s", perc);
  31. }
  32. void
  33. battery_state(char *out,
  34. const char *bat,
  35. uint32_t __unused _i,
  36. static_data_t *static_data)
  37. {
  38. int *fd = static_data->data;
  39. size_t i;
  40. char state[MAX_STATE];
  41. static const struct {
  42. const char *state;
  43. const char *symbol;
  44. } map[] = {
  45. { "Charging", BATTERY_CHARGING },
  46. { "Discharging", BATTERY_DISCHARGING },
  47. { "Not charging", BATTERY_FULL },
  48. };
  49. if (!static_data->cleanup) static_data->cleanup = fd_cleanup;
  50. if (!get_state(state, fd, bat)) ERRRET(out);
  51. for (i = 0; i < LEN(map); i++)
  52. if (!strcmp(map[i].state, state)) break;
  53. bprintf(out, "%s", (i == LEN(map)) ? BATTERY_UNKNOWN : map[i].symbol);
  54. }
  55. void
  56. battery_remaining(char *out,
  57. const char *bat,
  58. uint32_t __unused _i,
  59. static_data_t *static_data)
  60. {
  61. struct remaining *fds = static_data->data;
  62. char buf[JU_STR_SIZE];
  63. char state[MAX_STATE];
  64. uintmax_t m, h, charge_now, current_now;
  65. #define RESOLVE(S, F) *F = &S->F
  66. int RESOLVE(fds, status), RESOLVE(fds, charge), RESOLVE(fds, current);
  67. double timeleft;
  68. static const char *charge_arr[] = {
  69. "charge_now",
  70. "energy_now",
  71. };
  72. static const char *current_arr[] = {
  73. "current_now",
  74. "power_now",
  75. };
  76. if (!static_data->cleanup)
  77. static_data->cleanup = battery_remaining_cleanup;
  78. if (!get_state(state, status, bat)) ERRRET(out);
  79. #define pick_scan(B, C) \
  80. (pick(bat, C, C##_arr, LEN(C##_arr)) && eread(*C, WITH_LEN(buf)) \
  81. && esscanf(1, buf, "%ju", &C##_now))
  82. if (!strcmp(state, "Discharging")) {
  83. do {
  84. if (pick_scan(buf, charge) && pick_scan(buf, current))
  85. break;
  86. ERRRET(out);
  87. } while (0);
  88. if (!current_now) goto not_discharging;
  89. timeleft = (double)charge_now / (double)current_now;
  90. SAFE_ASSIGN(h, timeleft);
  91. SAFE_ASSIGN(m, (timeleft - (double)h) * 60);
  92. bprintf(out, "%juh %jum", h, m);
  93. return;
  94. }
  95. not_discharging:
  96. bprintf(out, "%s", BATTERY_REMAINING_NOT_DISCHARGING);
  97. }
  98. static inline uint8_t
  99. pick(const char *bat, int *fd, const char **arr, size_t len)
  100. {
  101. __typeof__(len) i;
  102. uint8_t ret = 0;
  103. int bat_fd = -1;
  104. if (*fd > 0) return fd_rewind(*fd);
  105. if ((bat_fd = sysfs_fd(SYS_CLASS, bat, NULL)) == -1) return 0;
  106. for (i = 0; i < len; i++) {
  107. if ((*fd = openat(bat_fd, arr[i], O_RDONLY | O_CLOEXEC))
  108. != -1) {
  109. ret = !0;
  110. goto end;
  111. }
  112. }
  113. end:
  114. if (bat_fd != -1) eclose(bat_fd);
  115. return ret;
  116. }
  117. static inline uint8_t
  118. get_state(char state[MAX_STATE], int *fd, const char *bat)
  119. {
  120. size_t readed;
  121. if (!sysfs_fd_or_rewind(fd, SYS_CLASS, bat, "status")) return 0;
  122. if (!eread_ret(readed, *fd, state, MAX_STATE)) return !0;
  123. state[--readed /* '\n' at the end */] = '\0';
  124. return !0;
  125. }
  126. static inline void
  127. battery_remaining_cleanup(void *ptr)
  128. {
  129. int fds[] = {
  130. ((struct remaining *)ptr)->status,
  131. ((struct remaining *)ptr)->charge,
  132. ((struct remaining *)ptr)->current,
  133. };
  134. for (uint8_t i = 0; i < LEN(fds); i++)
  135. if (!!fds[i]) eclose(fds[i]);
  136. }