apm_power.c 8.8 KB

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