battery_acpi.c 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. /*
  2. * Copyright (C) 2011 Michael Buesch <m@bues.ch>
  3. *
  4. * This program is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU General Public License
  6. * as published by the Free Software Foundation; either version 2
  7. * of the License, or (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. */
  14. #include "battery_acpi.h"
  15. #include "util.h"
  16. #include "fileaccess.h"
  17. #include "log.h"
  18. #include <limits.h>
  19. #include <errno.h>
  20. #define AC_BASEDIR "/bus/acpi/drivers/ac"
  21. #define BATT_BASEDIR "/bus/acpi/drivers/battery"
  22. static int battery_acpi_update(struct battery *b)
  23. {
  24. struct battery_acpi *ba = container_of(b, struct battery_acpi, battery);
  25. int value, err, value_changed = 0;
  26. struct fileaccess *file;
  27. if (strempty(ba->ac_online_filename)) {
  28. value = -1;
  29. } else {
  30. file = sysfs_file_open(O_RDONLY, ba->ac_online_filename);
  31. if (!file) {
  32. logerr("WARNING: Failed to open ac/online file\n");
  33. return -ETXTBSY;
  34. }
  35. err = file_read_int(file, &value, 10);
  36. file_close(file);
  37. if (err) {
  38. logerr("WARNING: Failed to read ac/online file\n");
  39. return -ETXTBSY;
  40. }
  41. }
  42. if (value != ba->on_ac) {
  43. ba->on_ac = value;
  44. value_changed = 1;
  45. }
  46. value = 0;
  47. file = sysfs_file_open(O_RDONLY, ba->charge_max_filename);
  48. if (file) {
  49. err = file_read_int(file, &value, 10);
  50. file_close(file);
  51. if (err) {
  52. logerr("WARNING: Failed to read charge_max file\n");
  53. return -ETXTBSY;
  54. }
  55. }
  56. if (value != ba->charge_max) {
  57. ba->charge_max = value;
  58. value_changed = 1;
  59. }
  60. value = 0;
  61. file = sysfs_file_open(O_RDONLY, ba->charge_now_filename);
  62. if (file) {
  63. err = file_read_int(file, &value, 10);
  64. file_close(file);
  65. if (err) {
  66. logerr("WARNING: Failed to read charge_now file\n");
  67. return -ETXTBSY;
  68. }
  69. }
  70. value = min(value, ba->charge_max);
  71. if (value != ba->charge_now) {
  72. ba->charge_now = value;
  73. value_changed = 1;
  74. }
  75. if (value_changed)
  76. battery_notify_state_change(b);
  77. return 0;
  78. }
  79. static int battery_acpi_on_ac(struct battery *b)
  80. {
  81. struct battery_acpi *ba = container_of(b, struct battery_acpi, battery);
  82. return ba->on_ac;
  83. }
  84. static int battery_acpi_max_level(struct battery *b)
  85. {
  86. struct battery_acpi *ba = container_of(b, struct battery_acpi, battery);
  87. return ba->charge_max;
  88. }
  89. static int battery_acpi_charge_level(struct battery *b)
  90. {
  91. struct battery_acpi *ba = container_of(b, struct battery_acpi, battery);
  92. return ba->charge_now;
  93. }
  94. static void battery_acpi_destroy(struct battery *b)
  95. {
  96. struct battery_acpi *ba = container_of(b, struct battery_acpi, battery);
  97. free(ba->ac_online_filename);
  98. free(ba->charge_max_filename);
  99. free(ba->charge_now_filename);
  100. free(ba);
  101. }
  102. static void find_ac_file(char *ac_file, size_t ac_file_size)
  103. {
  104. LIST_HEAD(dentries);
  105. struct dir_entry *dentry;
  106. int res, ok;
  107. strncat(ac_file, AC_BASEDIR, ac_file_size - strlen(ac_file) - 1);
  108. /* Find the AC-online file */
  109. res = list_sysfs_directory(&dentries, ac_file);
  110. if (res <= 0)
  111. goto error;
  112. ok = 0;
  113. list_for_each_entry(dentry, &dentries, list) {
  114. if (dentry->type == DT_LNK &&
  115. strncmp(dentry->name, "ACPI", 4) == 0) {
  116. strncat(ac_file, "/", ac_file_size - strlen(ac_file) - 1);
  117. strncat(ac_file, dentry->name, ac_file_size - strlen(ac_file) - 1);
  118. strncat(ac_file, "/power_supply", ac_file_size - strlen(ac_file) - 1);
  119. ok = 1;
  120. break;
  121. }
  122. }
  123. dir_entries_free(&dentries);
  124. if (!ok)
  125. goto error;
  126. res = list_sysfs_directory(&dentries, ac_file);
  127. if (res <= 0)
  128. goto error;
  129. ok = 0;
  130. list_for_each_entry(dentry, &dentries, list) {
  131. if (dentry->type == DT_DIR &&
  132. strncmp(dentry->name, "AC", 2) == 0) {
  133. strncat(ac_file, "/", ac_file_size - strlen(ac_file) - 1);
  134. strncat(ac_file, dentry->name, ac_file_size - strlen(ac_file) - 1);
  135. strncat(ac_file, "/online", ac_file_size - strlen(ac_file) - 1);
  136. ok = 1;
  137. break;
  138. }
  139. }
  140. dir_entries_free(&dentries);
  141. if (!ok)
  142. goto error;
  143. return;
  144. error:
  145. ac_file[0] = '\0';
  146. }
  147. static void find_battery_full_file(char *full_file, size_t full_file_size)
  148. {
  149. LIST_HEAD(dentries);
  150. struct dir_entry *dentry;
  151. int res, ok;
  152. strncat(full_file, BATT_BASEDIR, full_file_size - strlen(full_file) - 1);
  153. /* Find the battery-full file */
  154. res = list_sysfs_directory(&dentries, full_file);
  155. if (res <= 0)
  156. goto error;
  157. ok = 0;
  158. list_for_each_entry(dentry, &dentries, list) {
  159. if (dentry->type == DT_LNK &&
  160. strncmp(dentry->name, "PNP", 3) == 0) {
  161. strncat(full_file, "/", full_file_size - strlen(full_file) - 1);
  162. strncat(full_file, dentry->name, full_file_size - strlen(full_file) - 1);
  163. strncat(full_file, "/power_supply", full_file_size - strlen(full_file) - 1);
  164. ok = 1;
  165. break;
  166. }
  167. }
  168. dir_entries_free(&dentries);
  169. if (!ok)
  170. goto error;
  171. res = list_sysfs_directory(&dentries, full_file);
  172. if (res <= 0)
  173. goto error;
  174. ok = 0;
  175. list_for_each_entry(dentry, &dentries, list) {
  176. if (dentry->type == DT_DIR &&
  177. strncmp(dentry->name, "BAT", 3) == 0) {
  178. strncat(full_file, "/", full_file_size - strlen(full_file) - 1);
  179. strncat(full_file, dentry->name, full_file_size - strlen(full_file) - 1);
  180. ok = 1;
  181. break;
  182. }
  183. }
  184. dir_entries_free(&dentries);
  185. if (!ok)
  186. goto error;
  187. res = list_sysfs_directory(&dentries, full_file);
  188. if (res <= 0)
  189. goto error;
  190. ok = 0;
  191. list_for_each_entry(dentry, &dentries, list) {
  192. if (dentry->type == DT_REG &&
  193. (strcmp(dentry->name, "charge_full") == 0 ||
  194. strcmp(dentry->name, "energy_full") == 0)) {
  195. strncat(full_file, "/", full_file_size - strlen(full_file) - 1);
  196. strncat(full_file, dentry->name, full_file_size - strlen(full_file) - 1);
  197. ok = 1;
  198. break;
  199. }
  200. }
  201. dir_entries_free(&dentries);
  202. if (!ok)
  203. goto error;
  204. return;
  205. error:
  206. full_file[0] = '\0';
  207. }
  208. static void find_battery_now_file(char *now_file, size_t now_file_size)
  209. {
  210. LIST_HEAD(dentries);
  211. struct dir_entry *dentry;
  212. int res, ok;
  213. strncat(now_file, BATT_BASEDIR, now_file_size - strlen(now_file) - 1);
  214. /* Find the battery-now file */
  215. res = list_sysfs_directory(&dentries, now_file);
  216. if (res <= 0)
  217. goto error;
  218. ok = 0;
  219. list_for_each_entry(dentry, &dentries, list) {
  220. if (dentry->type == DT_LNK &&
  221. strncmp(dentry->name, "PNP", 3) == 0) {
  222. strncat(now_file, "/", now_file_size - strlen(now_file) - 1);
  223. strncat(now_file, dentry->name, now_file_size - strlen(now_file) - 1);
  224. strncat(now_file, "/power_supply", now_file_size - strlen(now_file) - 1);
  225. ok = 1;
  226. break;
  227. }
  228. }
  229. dir_entries_free(&dentries);
  230. if (!ok)
  231. goto error;
  232. res = list_sysfs_directory(&dentries, now_file);
  233. if (res <= 0)
  234. goto error;
  235. ok = 0;
  236. list_for_each_entry(dentry, &dentries, list) {
  237. if (dentry->type == DT_DIR &&
  238. strncmp(dentry->name, "BAT", 3) == 0) {
  239. strncat(now_file, "/", now_file_size - strlen(now_file) - 1);
  240. strncat(now_file, dentry->name, now_file_size - strlen(now_file) - 1);
  241. ok = 1;
  242. break;
  243. }
  244. }
  245. dir_entries_free(&dentries);
  246. if (!ok)
  247. goto error;
  248. res = list_sysfs_directory(&dentries, now_file);
  249. if (res <= 0)
  250. goto error;
  251. ok = 0;
  252. list_for_each_entry(dentry, &dentries, list) {
  253. if (dentry->type == DT_REG &&
  254. (strcmp(dentry->name, "charge_now") == 0 ||
  255. strcmp(dentry->name, "energy_now") == 0)) {
  256. strncat(now_file, "/", now_file_size - strlen(now_file) - 1);
  257. strncat(now_file, dentry->name, now_file_size - strlen(now_file) - 1);
  258. ok = 1;
  259. break;
  260. }
  261. }
  262. dir_entries_free(&dentries);
  263. if (!ok)
  264. goto error;
  265. return;
  266. error:
  267. now_file[0] = '\0';
  268. }
  269. static struct battery * battery_acpi_probe(void)
  270. {
  271. struct battery_acpi *ba;
  272. char ac_file[PATH_MAX + 1] = { 0, };
  273. char full_file[PATH_MAX + 1] = { 0, };
  274. char now_file[PATH_MAX + 1] = { 0, };
  275. find_ac_file(ac_file, sizeof(ac_file));
  276. find_battery_full_file(full_file, sizeof(full_file));
  277. find_battery_now_file(now_file, sizeof(now_file));
  278. if (strempty(full_file) || strempty(now_file))
  279. goto error;
  280. ba = zalloc(sizeof(*ba));
  281. if (!ba)
  282. goto error;
  283. battery_init(&ba->battery, "acpi");
  284. ba->battery.destroy = battery_acpi_destroy;
  285. ba->battery.update = battery_acpi_update;
  286. ba->battery.on_ac = battery_acpi_on_ac;
  287. ba->battery.max_level = battery_acpi_max_level;
  288. ba->battery.charge_level = battery_acpi_charge_level;
  289. ba->battery.poll_interval = 10000;
  290. ba->ac_online_filename = strdup(ac_file);
  291. ba->charge_max_filename = strdup(full_file);
  292. ba->charge_now_filename = strdup(now_file);
  293. return &ba->battery;
  294. error:
  295. return NULL;
  296. }
  297. BATTERY_PROBE(acpi, battery_acpi_probe);