battery.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. /* See LICENSE file for copyright and license details. */
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include "../slstatus.h"
  5. #include "../util.h"
  6. #if defined(__linux__)
  7. /*
  8. * https://www.kernel.org/doc/html/latest/power/power_supply_class.html
  9. */
  10. #include <limits.h>
  11. #include <stdint.h>
  12. #include <unistd.h>
  13. #define POWER_SUPPLY_CAPACITY "/sys/class/power_supply/%s/capacity"
  14. #define POWER_SUPPLY_STATUS "/sys/class/power_supply/%s/status"
  15. #define POWER_SUPPLY_CHARGE "/sys/class/power_supply/%s/charge_now"
  16. #define POWER_SUPPLY_ENERGY "/sys/class/power_supply/%s/energy_now"
  17. #define POWER_SUPPLY_CURRENT "/sys/class/power_supply/%s/current_now"
  18. #define POWER_SUPPLY_POWER "/sys/class/power_supply/%s/power_now"
  19. static const char *
  20. pick(const char *bat, const char *f1, const char *f2, char *path,
  21. size_t length)
  22. {
  23. if (esnprintf(path, length, f1, bat) > 0 &&
  24. access(path, R_OK) == 0)
  25. return f1;
  26. if (esnprintf(path, length, f2, bat) > 0 &&
  27. access(path, R_OK) == 0)
  28. return f2;
  29. return NULL;
  30. }
  31. const char *
  32. battery_perc(const char *bat)
  33. {
  34. int cap_perc;
  35. char path[PATH_MAX];
  36. if (esnprintf(path, sizeof(path), POWER_SUPPLY_CAPACITY, bat) < 0)
  37. return NULL;
  38. if (pscanf(path, "%d", &cap_perc) != 1)
  39. return NULL;
  40. return bprintf("%d", cap_perc);
  41. }
  42. const char *
  43. battery_state(const char *bat)
  44. {
  45. static struct {
  46. char *state;
  47. char *symbol;
  48. } map[] = {
  49. { "Charging", "+" },
  50. { "Discharging", "-" },
  51. { "Full", "o" },
  52. { "Not charging", "o" },
  53. };
  54. size_t i;
  55. char path[PATH_MAX], state[12];
  56. if (esnprintf(path, sizeof(path), POWER_SUPPLY_STATUS, bat) < 0)
  57. return NULL;
  58. if (pscanf(path, "%12[a-zA-Z ]", state) != 1)
  59. return NULL;
  60. for (i = 0; i < LEN(map); i++)
  61. if (!strcmp(map[i].state, state))
  62. break;
  63. return (i == LEN(map)) ? "?" : map[i].symbol;
  64. }
  65. const char *
  66. battery_remaining(const char *bat)
  67. {
  68. uintmax_t charge_now, current_now, m, h;
  69. double timeleft;
  70. char path[PATH_MAX], state[12];
  71. if (esnprintf(path, sizeof(path), POWER_SUPPLY_STATUS, bat) < 0)
  72. return NULL;
  73. if (pscanf(path, "%12[a-zA-Z ]", state) != 1)
  74. return NULL;
  75. if (!pick(bat, POWER_SUPPLY_CHARGE, POWER_SUPPLY_ENERGY, path,
  76. sizeof(path)) ||
  77. pscanf(path, "%ju", &charge_now) < 0)
  78. return NULL;
  79. if (!strcmp(state, "Discharging")) {
  80. if (!pick(bat, POWER_SUPPLY_CURRENT, POWER_SUPPLY_POWER, path,
  81. sizeof(path)) ||
  82. pscanf(path, "%ju", &current_now) < 0)
  83. return NULL;
  84. if (current_now == 0)
  85. return NULL;
  86. timeleft = (double)charge_now / (double)current_now;
  87. h = timeleft;
  88. m = (timeleft - (double)h) * 60;
  89. return bprintf("%juh %jum", h, m);
  90. }
  91. return "";
  92. }
  93. #elif defined(__OpenBSD__)
  94. #include <fcntl.h>
  95. #include <machine/apmvar.h>
  96. #include <sys/ioctl.h>
  97. #include <unistd.h>
  98. static int
  99. load_apm_power_info(struct apm_power_info *apm_info)
  100. {
  101. int fd;
  102. fd = open("/dev/apm", O_RDONLY);
  103. if (fd < 0) {
  104. warn("open '/dev/apm':");
  105. return 0;
  106. }
  107. memset(apm_info, 0, sizeof(struct apm_power_info));
  108. if (ioctl(fd, APM_IOC_GETPOWER, apm_info) < 0) {
  109. warn("ioctl 'APM_IOC_GETPOWER':");
  110. close(fd);
  111. return 0;
  112. }
  113. return close(fd), 1;
  114. }
  115. const char *
  116. battery_perc(const char *unused)
  117. {
  118. struct apm_power_info apm_info;
  119. if (load_apm_power_info(&apm_info))
  120. return bprintf("%d", apm_info.battery_life);
  121. return NULL;
  122. }
  123. const char *
  124. battery_state(const char *unused)
  125. {
  126. struct {
  127. unsigned int state;
  128. char *symbol;
  129. } map[] = {
  130. { APM_AC_ON, "+" },
  131. { APM_AC_OFF, "-" },
  132. };
  133. struct apm_power_info apm_info;
  134. size_t i;
  135. if (load_apm_power_info(&apm_info)) {
  136. for (i = 0; i < LEN(map); i++)
  137. if (map[i].state == apm_info.ac_state)
  138. break;
  139. return (i == LEN(map)) ? "?" : map[i].symbol;
  140. }
  141. return NULL;
  142. }
  143. const char *
  144. battery_remaining(const char *unused)
  145. {
  146. struct apm_power_info apm_info;
  147. unsigned int h, m;
  148. if (load_apm_power_info(&apm_info)) {
  149. if (apm_info.ac_state != APM_AC_ON) {
  150. h = apm_info.minutes_left / 60;
  151. m = apm_info.minutes_left % 60;
  152. return bprintf("%uh %02um", h, m);
  153. } else {
  154. return "";
  155. }
  156. }
  157. return NULL;
  158. }
  159. #elif defined(__FreeBSD__)
  160. #include <sys/sysctl.h>
  161. #define BATTERY_LIFE "hw.acpi.battery.life"
  162. #define BATTERY_STATE "hw.acpi.battery.state"
  163. #define BATTERY_TIME "hw.acpi.battery.time"
  164. const char *
  165. battery_perc(const char *unused)
  166. {
  167. int cap_perc;
  168. size_t len;
  169. len = sizeof(cap_perc);
  170. if (sysctlbyname(BATTERY_LIFE, &cap_perc, &len, NULL, 0) < 0 || !len)
  171. return NULL;
  172. return bprintf("%d", cap_perc);
  173. }
  174. const char *
  175. battery_state(const char *unused)
  176. {
  177. int state;
  178. size_t len;
  179. len = sizeof(state);
  180. if (sysctlbyname(BATTERY_STATE, &state, &len, NULL, 0) < 0 || !len)
  181. return NULL;
  182. switch (state) {
  183. case 0: /* FALLTHROUGH */
  184. case 2:
  185. return "+";
  186. case 1:
  187. return "-";
  188. default:
  189. return "?";
  190. }
  191. }
  192. const char *
  193. battery_remaining(const char *unused)
  194. {
  195. int rem;
  196. size_t len;
  197. len = sizeof(rem);
  198. if (sysctlbyname(BATTERY_TIME, &rem, &len, NULL, 0) < 0 || !len
  199. || rem < 0)
  200. return NULL;
  201. return bprintf("%uh %02um", rem / 60, rem % 60);
  202. }
  203. #elif defined(__NetBSD__)
  204. #define _NO_APM 1
  205. #define _PATH_APM_NORMAL "/dev/apm"
  206. #include <sys/types.h>
  207. #include <sys/time.h>
  208. #define ENVSYSUNITNAMES
  209. #include <sys/param.h>
  210. #include <sys/envsys.h>
  211. #include <paths.h>
  212. #include <stdlib.h>
  213. #include <string.h>
  214. #include <signal.h>
  215. #include <stdio.h>
  216. #include <unistd.h>
  217. #include <sys/file.h>
  218. #include <sys/ioctl.h>
  219. #ifndef _NO_APM
  220. #include <machine/apmvar.h>
  221. #else
  222. #define APM_AC_OFF 0x00
  223. #define APM_AC_ON 0x01
  224. #endif
  225. /*
  226. * pre: fd contains a valid file descriptor of an envsys(4) supporting device
  227. * && ns is the number of sensors
  228. * && etds and ebis are arrays of sufficient size
  229. * post: returns 0 and etds and ebis arrays are filled with sensor info
  230. * or returns -1 on failure
  231. */
  232. static int
  233. fillsensors(int fd, envsys_tre_data_t *etds, envsys_basic_info_t *ebis,
  234. size_t ns)
  235. {
  236. int i;
  237. for (i = 0; i < ns; ++i) {
  238. ebis[i].sensor = i;
  239. if (ioctl(fd, ENVSYS_GTREINFO, &ebis[i]) == -1) {
  240. warn("Can't get sensor info for sensor %d", i);
  241. return 0;
  242. }
  243. etds[i].sensor = i;
  244. if (ioctl(fd, ENVSYS_GTREDATA, &etds[i]) == -1) {
  245. warn("Can't get sensor data for sensor %d", i);
  246. return 0;
  247. }
  248. }
  249. return 1;
  250. }
  251. /*
  252. * pre: fd contains a valid file descriptor of an envsys(4) supporting device
  253. * post: returns the number of valid sensors provided by the device
  254. * or -1 on error
  255. */
  256. static size_t
  257. numsensors(int fd)
  258. {
  259. int count = 0, valid = 1;
  260. envsys_tre_data_t etd;
  261. etd.sensor = 0;
  262. while (valid) {
  263. if (ioctl(fd, ENVSYS_GTREDATA, &etd) == -1)
  264. err(1, "Can't get sensor data");
  265. valid = etd.validflags & ENVSYS_FVALID;
  266. if (valid)
  267. ++count;
  268. ++etd.sensor;
  269. }
  270. return count;
  271. }
  272. static envsys_tre_data_t *etds;
  273. static envsys_basic_info_t *ebis;
  274. static int *cetds;
  275. #if defined(_PATH_SYSMON) && __NetBSD_Version__ >= 106110000
  276. #define HAVE_NETBSD_ACPI
  277. #endif
  278. int first = 1;
  279. const char *
  280. battery_perc(const char *unused)
  281. {
  282. int fd, r, p;
  283. #ifndef _NO_APM
  284. struct apm_power_info apm_info;
  285. #endif
  286. int acpi;
  287. size_t ns;
  288. size_t cc;
  289. char *apmdev;
  290. int i;
  291. acpi = 0;
  292. apmdev = _PATH_APM_NORMAL;
  293. if ((fd = open(apmdev, O_RDONLY)) == -1) {
  294. #ifdef HAVE_NETBSD_ACPI
  295. apmdev = _PATH_SYSMON;
  296. fd = open(apmdev, O_RDONLY);
  297. acpi = 1;
  298. #endif
  299. }
  300. if (fd < 0) {
  301. fprintf(stderr, "slstatus: cannot open %s device\n", apmdev);
  302. exit(1);
  303. }
  304. if (acpi) {
  305. #ifdef HAVE_NETBSD_ACPI
  306. if ((ns = numsensors(fd)) == 0) {
  307. fprintf(stderr, "slstatus: no sensors found\n");
  308. exit(1);
  309. }
  310. if (first) {
  311. cetds = (int *)malloc(ns * sizeof(int));
  312. etds = (envsys_tre_data_t *)malloc(ns * sizeof(envsys_tre_data_t));
  313. ebis = (envsys_basic_info_t *)malloc(ns * sizeof(envsys_basic_info_t));
  314. if ((cetds == NULL) || (etds == NULL) || (ebis == NULL)) {
  315. err(1, "Out of memory");
  316. }
  317. }
  318. fillsensors(fd, etds, ebis, ns);
  319. #endif
  320. #ifndef _NO_APM
  321. } else {
  322. memset(&info, 0, sizeof(info));
  323. if (ioctl(fd, APM_IOC_GETPOWER, &info) != 0) {
  324. fprintf(stderr, "slstatus: ioctl APM_IOC_GETPOWER failed\n");
  325. exit(1);
  326. }
  327. #endif
  328. }
  329. close(fd);
  330. if (acpi) {
  331. #ifdef HAVE_NETBSD_ACPI
  332. int32_t rtot = 0, maxtot = 0;
  333. int have_pct = 0;
  334. p = APM_AC_ON;
  335. for (i = 0 ; i < ns ; i++) {
  336. if ((etds[i].validflags & ENVSYS_FCURVALID) == 0)
  337. continue;
  338. cc = strlen(ebis[i].desc);
  339. if (strncmp(ebis[i].desc, "acpibat", 7) == 0 &&
  340. (strcmp(&ebis[i].desc[cc - 7], " charge") == 0 ||
  341. strcmp(&ebis[i].desc[cc - 7], " energy") == 0)) {
  342. rtot += etds[i].cur.data_s;
  343. maxtot += etds[i].max.data_s;
  344. }
  345. /*
  346. * XXX: We should use acpiacad driver and look for
  347. * " connected", but that's broken on some machines
  348. * and we want this to work everywhere. With this
  349. * we will occasionally catch a machine conditioning
  350. * a battery while connected, while other machines take
  351. * 10-15 seconds to switch from "charging" to
  352. * "discharging" and vice versa, but this is the best
  353. * compromise.
  354. */
  355. if ((ebis[i].units == ENVSYS_SWATTS || ebis[i].units == ENVSYS_SAMPS) &&
  356. etds[i].cur.data_s &&
  357. strncmp(ebis[i].desc, "acpibat", 7) == 0 &&
  358. strcmp(&ebis[i].desc[cc - 14], "discharge rate") == 0) {
  359. p = APM_AC_OFF;
  360. }
  361. if (ebis[i].units == ENVSYS_INTEGER &&
  362. strcmp(ebis[i].desc, "battery percent") == 0) {
  363. have_pct = 1;
  364. r = etds[i].cur.data_s;
  365. }
  366. if (ebis[i].units == ENVSYS_INDICATOR &&
  367. strcmp(ebis[i].desc, "ACIN present") == 0 &&
  368. etds[i].cur.data_s == 0) {
  369. p = APM_AC_OFF;
  370. }
  371. }
  372. if (!have_pct)
  373. r = (rtot * 100.0) / maxtot;
  374. #endif
  375. }
  376. if(p == APM_AC_OFF) {
  377. return bprintf("-%d", r);
  378. } else {
  379. return bprintf("+%d", r);
  380. }
  381. }
  382. #endif