apm_power.c 10 KB

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