apm_power.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  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, &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. switch (source) {
  118. case SOURCE_CHARGE:
  119. full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
  120. full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
  121. empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
  122. empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
  123. cur_avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
  124. cur_now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
  125. break;
  126. case SOURCE_ENERGY:
  127. full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
  128. full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
  129. empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
  130. empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
  131. cur_avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
  132. cur_now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
  133. break;
  134. case SOURCE_VOLTAGE:
  135. full_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX;
  136. full_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
  137. empty_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN;
  138. empty_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
  139. cur_avg_prop = POWER_SUPPLY_PROP_VOLTAGE_AVG;
  140. cur_now_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
  141. break;
  142. default:
  143. printk(KERN_ERR "Unsupported source: %d\n", source);
  144. return -1;
  145. }
  146. if (_MPSY_PROP(full_prop, &full)) {
  147. /* if battery can't report this property, use design value */
  148. if (_MPSY_PROP(full_design_prop, &full))
  149. return -1;
  150. }
  151. if (_MPSY_PROP(empty_prop, &empty)) {
  152. /* if battery can't report this property, use design value */
  153. if (_MPSY_PROP(empty_design_prop, &empty))
  154. empty.intval = 0;
  155. }
  156. if (_MPSY_PROP(cur_avg_prop, &cur)) {
  157. /* if battery can't report average value, use momentary */
  158. if (_MPSY_PROP(cur_now_prop, &cur))
  159. return -1;
  160. }
  161. if (status == POWER_SUPPLY_STATUS_CHARGING)
  162. return ((cur.intval - full.intval) * 60L) / I.intval;
  163. else
  164. return -((cur.intval - empty.intval) * 60L) / I.intval;
  165. }
  166. static int calculate_time(int status)
  167. {
  168. int time;
  169. time = do_calculate_time(status, SOURCE_ENERGY);
  170. if (time != -1)
  171. return time;
  172. time = do_calculate_time(status, SOURCE_CHARGE);
  173. if (time != -1)
  174. return time;
  175. time = do_calculate_time(status, SOURCE_VOLTAGE);
  176. if (time != -1)
  177. return time;
  178. return -1;
  179. }
  180. static int calculate_capacity(enum apm_source source)
  181. {
  182. enum power_supply_property full_prop, empty_prop;
  183. enum power_supply_property full_design_prop, empty_design_prop;
  184. enum power_supply_property now_prop, avg_prop;
  185. union power_supply_propval empty, full, cur;
  186. int ret;
  187. switch (source) {
  188. case SOURCE_CHARGE:
  189. full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
  190. empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
  191. full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
  192. empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN;
  193. now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
  194. avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
  195. break;
  196. case SOURCE_ENERGY:
  197. full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
  198. empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
  199. full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
  200. empty_design_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN;
  201. now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
  202. avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
  203. case SOURCE_VOLTAGE:
  204. full_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX;
  205. empty_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN;
  206. full_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
  207. empty_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
  208. now_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
  209. avg_prop = POWER_SUPPLY_PROP_VOLTAGE_AVG;
  210. break;
  211. default:
  212. printk(KERN_ERR "Unsupported source: %d\n", source);
  213. return -1;
  214. }
  215. if (_MPSY_PROP(full_prop, &full)) {
  216. /* if battery can't report this property, use design value */
  217. if (_MPSY_PROP(full_design_prop, &full))
  218. return -1;
  219. }
  220. if (_MPSY_PROP(avg_prop, &cur)) {
  221. /* if battery can't report average value, use momentary */
  222. if (_MPSY_PROP(now_prop, &cur))
  223. return -1;
  224. }
  225. if (_MPSY_PROP(empty_prop, &empty)) {
  226. /* if battery can't report this property, use design value */
  227. if (_MPSY_PROP(empty_design_prop, &empty))
  228. empty.intval = 0;
  229. }
  230. if (full.intval - empty.intval)
  231. ret = ((cur.intval - empty.intval) * 100L) /
  232. (full.intval - empty.intval);
  233. else
  234. return -1;
  235. if (ret > 100)
  236. return 100;
  237. else if (ret < 0)
  238. return 0;
  239. return ret;
  240. }
  241. static void apm_battery_apm_get_power_status(struct apm_power_info *info)
  242. {
  243. union power_supply_propval status;
  244. union power_supply_propval capacity, time_to_full, time_to_empty;
  245. mutex_lock(&apm_mutex);
  246. find_main_battery();
  247. if (!main_battery) {
  248. mutex_unlock(&apm_mutex);
  249. return;
  250. }
  251. /* status */
  252. if (MPSY_PROP(STATUS, &status))
  253. status.intval = POWER_SUPPLY_STATUS_UNKNOWN;
  254. /* ac line status */
  255. if ((status.intval == POWER_SUPPLY_STATUS_CHARGING) ||
  256. (status.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) ||
  257. (status.intval == POWER_SUPPLY_STATUS_FULL))
  258. info->ac_line_status = APM_AC_ONLINE;
  259. else
  260. info->ac_line_status = APM_AC_OFFLINE;
  261. /* battery life (i.e. capacity, in percents) */
  262. if (MPSY_PROP(CAPACITY, &capacity) == 0) {
  263. info->battery_life = capacity.intval;
  264. } else {
  265. /* try calculate using energy */
  266. info->battery_life = calculate_capacity(SOURCE_ENERGY);
  267. /* if failed try calculate using charge instead */
  268. if (info->battery_life == -1)
  269. info->battery_life = calculate_capacity(SOURCE_CHARGE);
  270. if (info->battery_life == -1)
  271. info->battery_life = calculate_capacity(SOURCE_VOLTAGE);
  272. }
  273. /* charging status */
  274. if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
  275. info->battery_status = APM_BATTERY_STATUS_CHARGING;
  276. } else {
  277. if (info->battery_life > 50)
  278. info->battery_status = APM_BATTERY_STATUS_HIGH;
  279. else if (info->battery_life > 5)
  280. info->battery_status = APM_BATTERY_STATUS_LOW;
  281. else
  282. info->battery_status = APM_BATTERY_STATUS_CRITICAL;
  283. }
  284. info->battery_flag = info->battery_status;
  285. /* time */
  286. info->units = APM_UNITS_MINS;
  287. if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
  288. if (!MPSY_PROP(TIME_TO_FULL_AVG, &time_to_full) ||
  289. !MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full))
  290. info->time = time_to_full.intval / 60;
  291. else
  292. info->time = calculate_time(status.intval);
  293. } else {
  294. if (!MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty) ||
  295. !MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty))
  296. info->time = time_to_empty.intval / 60;
  297. else
  298. info->time = calculate_time(status.intval);
  299. }
  300. mutex_unlock(&apm_mutex);
  301. }
  302. static int __init apm_battery_init(void)
  303. {
  304. printk(KERN_INFO "APM Battery Driver\n");
  305. apm_get_power_status = apm_battery_apm_get_power_status;
  306. return 0;
  307. }
  308. static void __exit apm_battery_exit(void)
  309. {
  310. apm_get_power_status = NULL;
  311. }
  312. module_init(apm_battery_init);
  313. module_exit(apm_battery_exit);
  314. MODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>");
  315. MODULE_DESCRIPTION("APM emulation driver for battery monitoring class");
  316. MODULE_LICENSE("GPL");