apm_power.c 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. /*
  2. * Copyright © 2007 Anton Vorontsov <cbou@mail.ru>
  3. * Copyright © 2007 Eugeny Boger <eugenyboger@dgap.mipt.ru>
  4. *
  5. * Author: Eugeny Boger <eugenyboger@dgap.mipt.ru>
  6. *
  7. * Use consistent with the GNU GPL is permitted,
  8. * provided that this copyright notice is
  9. * preserved in its entirety in all copies and derived works.
  10. */
  11. #include <linux/module.h>
  12. #include <linux/power_supply.h>
  13. #include <linux/apm-emulation.h>
  14. #define PSY_PROP(psy, prop, val) psy->get_property(psy, \
  15. POWER_SUPPLY_PROP_##prop, val)
  16. #define _MPSY_PROP(prop, val) main_battery->get_property(main_battery, \
  17. prop, val)
  18. #define MPSY_PROP(prop, val) _MPSY_PROP(POWER_SUPPLY_PROP_##prop, val)
  19. static struct power_supply *main_battery;
  20. static void find_main_battery(void)
  21. {
  22. struct device *dev;
  23. struct power_supply *bat = NULL;
  24. struct power_supply *max_charge_bat = NULL;
  25. struct power_supply *max_energy_bat = NULL;
  26. union power_supply_propval full;
  27. int max_charge = 0;
  28. int max_energy = 0;
  29. main_battery = NULL;
  30. list_for_each_entry(dev, &power_supply_class->devices, node) {
  31. bat = dev_get_drvdata(dev);
  32. if (bat->use_for_apm) {
  33. /* nice, we explicitly asked to report this battery. */
  34. main_battery = bat;
  35. return;
  36. }
  37. if (!PSY_PROP(bat, CHARGE_FULL_DESIGN, &full) ||
  38. !PSY_PROP(bat, CHARGE_FULL, &full)) {
  39. if (full.intval > max_charge) {
  40. max_charge_bat = bat;
  41. max_charge = full.intval;
  42. }
  43. } else if (!PSY_PROP(bat, ENERGY_FULL_DESIGN, &full) ||
  44. !PSY_PROP(bat, ENERGY_FULL, &full)) {
  45. if (full.intval > max_energy) {
  46. max_energy_bat = bat;
  47. max_energy = full.intval;
  48. }
  49. }
  50. }
  51. if ((max_energy_bat && max_charge_bat) &&
  52. (max_energy_bat != max_charge_bat)) {
  53. /* try guess battery with more capacity */
  54. if (!PSY_PROP(max_charge_bat, VOLTAGE_MAX_DESIGN, &full)) {
  55. if (max_energy > max_charge * full.intval)
  56. main_battery = max_energy_bat;
  57. else
  58. main_battery = max_charge_bat;
  59. } else if (!PSY_PROP(max_energy_bat, VOLTAGE_MAX_DESIGN,
  60. &full)) {
  61. if (max_charge > max_energy / full.intval)
  62. main_battery = max_charge_bat;
  63. else
  64. main_battery = max_energy_bat;
  65. } else {
  66. /* give up, choice any */
  67. main_battery = max_energy_bat;
  68. }
  69. } else if (max_charge_bat) {
  70. main_battery = max_charge_bat;
  71. } else if (max_energy_bat) {
  72. main_battery = max_energy_bat;
  73. } else {
  74. /* give up, try the last if any */
  75. main_battery = bat;
  76. }
  77. }
  78. static int calculate_time(int status, int using_charge)
  79. {
  80. union power_supply_propval full;
  81. union power_supply_propval empty;
  82. union power_supply_propval cur;
  83. union power_supply_propval I;
  84. enum power_supply_property full_prop;
  85. enum power_supply_property full_design_prop;
  86. enum power_supply_property empty_prop;
  87. enum power_supply_property empty_design_prop;
  88. enum power_supply_property cur_avg_prop;
  89. enum power_supply_property cur_now_prop;
  90. if (MPSY_PROP(CURRENT_AVG, &I)) {
  91. /* if battery can't report average value, use momentary */
  92. if (MPSY_PROP(CURRENT_NOW, &I))
  93. return -1;
  94. }
  95. if (using_charge) {
  96. full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
  97. full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
  98. empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
  99. empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
  100. cur_avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
  101. cur_now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
  102. } else {
  103. full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
  104. full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
  105. empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
  106. empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
  107. cur_avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
  108. cur_now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
  109. }
  110. if (_MPSY_PROP(full_prop, &full)) {
  111. /* if battery can't report this property, use design value */
  112. if (_MPSY_PROP(full_design_prop, &full))
  113. return -1;
  114. }
  115. if (_MPSY_PROP(empty_prop, &empty)) {
  116. /* if battery can't report this property, use design value */
  117. if (_MPSY_PROP(empty_design_prop, &empty))
  118. empty.intval = 0;
  119. }
  120. if (_MPSY_PROP(cur_avg_prop, &cur)) {
  121. /* if battery can't report average value, use momentary */
  122. if (_MPSY_PROP(cur_now_prop, &cur))
  123. return -1;
  124. }
  125. if (status == POWER_SUPPLY_STATUS_CHARGING)
  126. return ((cur.intval - full.intval) * 60L) / I.intval;
  127. else
  128. return -((cur.intval - empty.intval) * 60L) / I.intval;
  129. }
  130. static int calculate_capacity(int using_charge)
  131. {
  132. enum power_supply_property full_prop, empty_prop;
  133. enum power_supply_property full_design_prop, empty_design_prop;
  134. enum power_supply_property now_prop, avg_prop;
  135. union power_supply_propval empty, full, cur;
  136. int ret;
  137. if (using_charge) {
  138. full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
  139. empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
  140. full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
  141. empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN;
  142. now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
  143. avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
  144. } else {
  145. full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
  146. empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
  147. full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
  148. empty_design_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN;
  149. now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
  150. avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
  151. }
  152. if (_MPSY_PROP(full_prop, &full)) {
  153. /* if battery can't report this property, use design value */
  154. if (_MPSY_PROP(full_design_prop, &full))
  155. return -1;
  156. }
  157. if (_MPSY_PROP(avg_prop, &cur)) {
  158. /* if battery can't report average value, use momentary */
  159. if (_MPSY_PROP(now_prop, &cur))
  160. return -1;
  161. }
  162. if (_MPSY_PROP(empty_prop, &empty)) {
  163. /* if battery can't report this property, use design value */
  164. if (_MPSY_PROP(empty_design_prop, &empty))
  165. empty.intval = 0;
  166. }
  167. if (full.intval - empty.intval)
  168. ret = ((cur.intval - empty.intval) * 100L) /
  169. (full.intval - empty.intval);
  170. else
  171. return -1;
  172. if (ret > 100)
  173. return 100;
  174. else if (ret < 0)
  175. return 0;
  176. return ret;
  177. }
  178. static void apm_battery_apm_get_power_status(struct apm_power_info *info)
  179. {
  180. union power_supply_propval status;
  181. union power_supply_propval capacity, time_to_full, time_to_empty;
  182. down(&power_supply_class->sem);
  183. find_main_battery();
  184. if (!main_battery) {
  185. up(&power_supply_class->sem);
  186. return;
  187. }
  188. /* status */
  189. if (MPSY_PROP(STATUS, &status))
  190. status.intval = POWER_SUPPLY_STATUS_UNKNOWN;
  191. /* ac line status */
  192. if ((status.intval == POWER_SUPPLY_STATUS_CHARGING) ||
  193. (status.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) ||
  194. (status.intval == POWER_SUPPLY_STATUS_FULL))
  195. info->ac_line_status = APM_AC_ONLINE;
  196. else
  197. info->ac_line_status = APM_AC_OFFLINE;
  198. /* battery life (i.e. capacity, in percents) */
  199. if (MPSY_PROP(CAPACITY, &capacity) == 0) {
  200. info->battery_life = capacity.intval;
  201. } else {
  202. /* try calculate using energy */
  203. info->battery_life = calculate_capacity(0);
  204. /* if failed try calculate using charge instead */
  205. if (info->battery_life == -1)
  206. info->battery_life = calculate_capacity(1);
  207. }
  208. /* charging status */
  209. if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
  210. info->battery_status = APM_BATTERY_STATUS_CHARGING;
  211. } else {
  212. if (info->battery_life > 50)
  213. info->battery_status = APM_BATTERY_STATUS_HIGH;
  214. else if (info->battery_life > 5)
  215. info->battery_status = APM_BATTERY_STATUS_LOW;
  216. else
  217. info->battery_status = APM_BATTERY_STATUS_CRITICAL;
  218. }
  219. info->battery_flag = info->battery_status;
  220. /* time */
  221. info->units = APM_UNITS_MINS;
  222. if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
  223. if (!MPSY_PROP(TIME_TO_FULL_AVG, &time_to_full) ||
  224. !MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full)) {
  225. info->time = time_to_full.intval / 60;
  226. } else {
  227. info->time = calculate_time(status.intval, 0);
  228. if (info->time == -1)
  229. info->time = calculate_time(status.intval, 1);
  230. }
  231. } else {
  232. if (!MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty) ||
  233. !MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty)) {
  234. info->time = time_to_empty.intval / 60;
  235. } else {
  236. info->time = calculate_time(status.intval, 0);
  237. if (info->time == -1)
  238. info->time = calculate_time(status.intval, 1);
  239. }
  240. }
  241. up(&power_supply_class->sem);
  242. }
  243. static int __init apm_battery_init(void)
  244. {
  245. printk(KERN_INFO "APM Battery Driver\n");
  246. apm_get_power_status = apm_battery_apm_get_power_status;
  247. return 0;
  248. }
  249. static void __exit apm_battery_exit(void)
  250. {
  251. apm_get_power_status = NULL;
  252. }
  253. module_init(apm_battery_init);
  254. module_exit(apm_battery_exit);
  255. MODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>");
  256. MODULE_DESCRIPTION("APM emulation driver for battery monitoring class");
  257. MODULE_LICENSE("GPL");