apm_power.c 10 KB

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