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