2 * Copyright © 2007 Anton Vorontsov <cbou@mail.ru>
3 * Copyright © 2007 Eugeny Boger <eugenyboger@dgap.mipt.ru>
5 * Author: Eugeny Boger <eugenyboger@dgap.mipt.ru>
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.
12 #include <linux/module.h>
13 #include <linux/power_supply.h>
14 #include <linux/apm-emulation.h>
16 #define PSY_PROP(psy, prop, val) psy->get_property(psy, \
17 POWER_SUPPLY_PROP_##prop, val)
19 #define _MPSY_PROP(prop, val) main_battery->get_property(main_battery, \
22 #define MPSY_PROP(prop, val) _MPSY_PROP(POWER_SUPPLY_PROP_##prop, val)
24 static struct power_supply *main_battery;
26 static void find_main_battery(void)
29 struct power_supply *bat, *batm;
30 union power_supply_propval full;
35 list_for_each_entry(dev, &power_supply_class->devices, node) {
36 bat = dev_get_drvdata(dev);
37 /* If none of battery devices cantains 'use_for_apm' flag,
38 choice one with maximum design charge */
39 if (!PSY_PROP(bat, CHARGE_FULL_DESIGN, &full)) {
40 if (full.intval > max_charge) {
42 max_charge = full.intval;
53 static int calculate_time(int status)
55 union power_supply_propval charge_full, charge_empty;
56 union power_supply_propval charge, I;
58 if (MPSY_PROP(CHARGE_FULL, &charge_full)) {
59 /* if battery can't report this property, use design value */
60 if (MPSY_PROP(CHARGE_FULL_DESIGN, &charge_full))
64 if (MPSY_PROP(CHARGE_EMPTY, &charge_empty)) {
65 /* if battery can't report this property, use design value */
66 if (MPSY_PROP(CHARGE_EMPTY_DESIGN, &charge_empty))
67 charge_empty.intval = 0;
70 if (MPSY_PROP(CHARGE_AVG, &charge)) {
71 /* if battery can't report average value, use momentary */
72 if (MPSY_PROP(CHARGE_NOW, &charge))
76 if (MPSY_PROP(CURRENT_AVG, &I)) {
77 /* if battery can't report average value, use momentary */
78 if (MPSY_PROP(CURRENT_NOW, &I))
82 if (status == POWER_SUPPLY_STATUS_CHARGING)
83 return ((charge.intval - charge_full.intval) * 60L) /
86 return -((charge.intval - charge_empty.intval) * 60L) /
90 static int calculate_capacity(int using_charge)
92 enum power_supply_property full_prop, empty_prop;
93 enum power_supply_property full_design_prop, empty_design_prop;
94 enum power_supply_property now_prop, avg_prop;
95 union power_supply_propval empty, full, cur;
99 full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
100 empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
101 full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
102 empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN;
103 now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
104 avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
106 full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
107 empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
108 full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
109 empty_design_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN;
110 now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
111 avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
114 if (_MPSY_PROP(full_prop, &full)) {
115 /* if battery can't report this property, use design value */
116 if (_MPSY_PROP(full_design_prop, &full))
120 if (_MPSY_PROP(avg_prop, &cur)) {
121 /* if battery can't report average value, use momentary */
122 if (_MPSY_PROP(now_prop, &cur))
126 if (_MPSY_PROP(empty_prop, &empty)) {
127 /* if battery can't report this property, use design value */
128 if (_MPSY_PROP(empty_design_prop, &empty))
132 if (full.intval - empty.intval)
133 ret = ((cur.intval - empty.intval) * 100L) /
134 (full.intval - empty.intval);
146 static void apm_battery_apm_get_power_status(struct apm_power_info *info)
148 union power_supply_propval status;
149 union power_supply_propval capacity, time_to_full, time_to_empty;
151 down(&power_supply_class->sem);
154 up(&power_supply_class->sem);
160 if (MPSY_PROP(STATUS, &status))
161 status.intval = POWER_SUPPLY_STATUS_UNKNOWN;
165 if ((status.intval == POWER_SUPPLY_STATUS_CHARGING) ||
166 (status.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) ||
167 (status.intval == POWER_SUPPLY_STATUS_FULL))
168 info->ac_line_status = APM_AC_ONLINE;
170 info->ac_line_status = APM_AC_OFFLINE;
172 /* battery life (i.e. capacity, in percents) */
174 if (MPSY_PROP(CAPACITY, &capacity) == 0) {
175 info->battery_life = capacity.intval;
177 /* try calculate using energy */
178 info->battery_life = calculate_capacity(0);
179 /* if failed try calculate using charge instead */
180 if (info->battery_life == -1)
181 info->battery_life = calculate_capacity(1);
184 /* charging status */
186 if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
187 info->battery_status = APM_BATTERY_STATUS_CHARGING;
189 if (info->battery_life > 50)
190 info->battery_status = APM_BATTERY_STATUS_HIGH;
191 else if (info->battery_life > 5)
192 info->battery_status = APM_BATTERY_STATUS_LOW;
194 info->battery_status = APM_BATTERY_STATUS_CRITICAL;
196 info->battery_flag = info->battery_status;
200 info->units = APM_UNITS_MINS;
202 if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
203 if (MPSY_PROP(TIME_TO_FULL_AVG, &time_to_full)) {
204 if (MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full))
205 info->time = calculate_time(status.intval);
207 info->time = time_to_full.intval / 60;
210 if (MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty)) {
211 if (MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty))
212 info->time = calculate_time(status.intval);
214 info->time = time_to_empty.intval / 60;
218 up(&power_supply_class->sem);
221 static int __init apm_battery_init(void)
223 printk(KERN_INFO "APM Battery Driver\n");
225 apm_get_power_status = apm_battery_apm_get_power_status;
229 static void __exit apm_battery_exit(void)
231 apm_get_power_status = NULL;
234 module_init(apm_battery_init);
235 module_exit(apm_battery_exit);
237 MODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>");
238 MODULE_DESCRIPTION("APM emulation driver for battery monitoring class");
239 MODULE_LICENSE("GPL");