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>
17 #define PSY_PROP(psy, prop, val) psy->get_property(psy, \
18 POWER_SUPPLY_PROP_##prop, val)
20 #define _MPSY_PROP(prop, val) main_battery->get_property(main_battery, \
23 #define MPSY_PROP(prop, val) _MPSY_PROP(POWER_SUPPLY_PROP_##prop, val)
25 static DEFINE_MUTEX(apm_mutex
);
26 static struct power_supply
*main_battery
;
34 struct find_bat_param
{
35 struct power_supply
*main
;
36 struct power_supply
*bat
;
37 struct power_supply
*max_charge_bat
;
38 struct power_supply
*max_energy_bat
;
39 union power_supply_propval full
;
44 static int __find_main_battery(struct device
*dev
, void *data
)
46 struct find_bat_param
*bp
= (struct find_bat_param
*)data
;
48 bp
->bat
= dev_get_drvdata(dev
);
50 if (bp
->bat
->use_for_apm
) {
51 /* nice, we explicitly asked to report this battery. */
56 if (!PSY_PROP(bp
->bat
, CHARGE_FULL_DESIGN
, &bp
->full
) ||
57 !PSY_PROP(bp
->bat
, CHARGE_FULL
, &bp
->full
)) {
58 if (bp
->full
.intval
> bp
->max_charge
) {
59 bp
->max_charge_bat
= bp
->bat
;
60 bp
->max_charge
= bp
->full
.intval
;
62 } else if (!PSY_PROP(bp
->bat
, ENERGY_FULL_DESIGN
, &bp
->full
) ||
63 !PSY_PROP(bp
->bat
, ENERGY_FULL
, &bp
->full
)) {
64 if (bp
->full
.intval
> bp
->max_energy
) {
65 bp
->max_energy_bat
= bp
->bat
;
66 bp
->max_energy
= bp
->full
.intval
;
72 static void find_main_battery(void)
74 struct find_bat_param bp
;
77 memset(&bp
, 0, sizeof(struct find_bat_param
));
79 bp
.main
= main_battery
;
81 error
= class_for_each_device(power_supply_class
, &bp
,
84 main_battery
= bp
.main
;
88 if ((bp
.max_energy_bat
&& bp
.max_charge_bat
) &&
89 (bp
.max_energy_bat
!= bp
.max_charge_bat
)) {
90 /* try guess battery with more capacity */
91 if (!PSY_PROP(bp
.max_charge_bat
, VOLTAGE_MAX_DESIGN
,
93 if (bp
.max_energy
> bp
.max_charge
* bp
.full
.intval
)
94 main_battery
= bp
.max_energy_bat
;
96 main_battery
= bp
.max_charge_bat
;
97 } else if (!PSY_PROP(bp
.max_energy_bat
, VOLTAGE_MAX_DESIGN
,
99 if (bp
.max_charge
> bp
.max_energy
/ bp
.full
.intval
)
100 main_battery
= bp
.max_charge_bat
;
102 main_battery
= bp
.max_energy_bat
;
104 /* give up, choice any */
105 main_battery
= bp
.max_energy_bat
;
107 } else if (bp
.max_charge_bat
) {
108 main_battery
= bp
.max_charge_bat
;
109 } else if (bp
.max_energy_bat
) {
110 main_battery
= bp
.max_energy_bat
;
112 /* give up, try the last if any */
113 main_battery
= bp
.bat
;
117 static int do_calculate_time(int status
, enum apm_source source
)
119 union power_supply_propval full
;
120 union power_supply_propval empty
;
121 union power_supply_propval cur
;
122 union power_supply_propval I
;
123 enum power_supply_property full_prop
;
124 enum power_supply_property full_design_prop
;
125 enum power_supply_property empty_prop
;
126 enum power_supply_property empty_design_prop
;
127 enum power_supply_property cur_avg_prop
;
128 enum power_supply_property cur_now_prop
;
130 if (MPSY_PROP(CURRENT_AVG
, &I
)) {
131 /* if battery can't report average value, use momentary */
132 if (MPSY_PROP(CURRENT_NOW
, &I
))
138 full_prop
= POWER_SUPPLY_PROP_CHARGE_FULL
;
139 full_design_prop
= POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN
;
140 empty_prop
= POWER_SUPPLY_PROP_CHARGE_EMPTY
;
141 empty_design_prop
= POWER_SUPPLY_PROP_CHARGE_EMPTY
;
142 cur_avg_prop
= POWER_SUPPLY_PROP_CHARGE_AVG
;
143 cur_now_prop
= POWER_SUPPLY_PROP_CHARGE_NOW
;
146 full_prop
= POWER_SUPPLY_PROP_ENERGY_FULL
;
147 full_design_prop
= POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN
;
148 empty_prop
= POWER_SUPPLY_PROP_ENERGY_EMPTY
;
149 empty_design_prop
= POWER_SUPPLY_PROP_CHARGE_EMPTY
;
150 cur_avg_prop
= POWER_SUPPLY_PROP_ENERGY_AVG
;
151 cur_now_prop
= POWER_SUPPLY_PROP_ENERGY_NOW
;
154 full_prop
= POWER_SUPPLY_PROP_VOLTAGE_MAX
;
155 full_design_prop
= POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN
;
156 empty_prop
= POWER_SUPPLY_PROP_VOLTAGE_MIN
;
157 empty_design_prop
= POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN
;
158 cur_avg_prop
= POWER_SUPPLY_PROP_VOLTAGE_AVG
;
159 cur_now_prop
= POWER_SUPPLY_PROP_VOLTAGE_NOW
;
162 printk(KERN_ERR
"Unsupported source: %d\n", source
);
166 if (_MPSY_PROP(full_prop
, &full
)) {
167 /* if battery can't report this property, use design value */
168 if (_MPSY_PROP(full_design_prop
, &full
))
172 if (_MPSY_PROP(empty_prop
, &empty
)) {
173 /* if battery can't report this property, use design value */
174 if (_MPSY_PROP(empty_design_prop
, &empty
))
178 if (_MPSY_PROP(cur_avg_prop
, &cur
)) {
179 /* if battery can't report average value, use momentary */
180 if (_MPSY_PROP(cur_now_prop
, &cur
))
184 if (status
== POWER_SUPPLY_STATUS_CHARGING
)
185 return ((cur
.intval
- full
.intval
) * 60L) / I
.intval
;
187 return -((cur
.intval
- empty
.intval
) * 60L) / I
.intval
;
190 static int calculate_time(int status
)
194 time
= do_calculate_time(status
, SOURCE_ENERGY
);
198 time
= do_calculate_time(status
, SOURCE_CHARGE
);
202 time
= do_calculate_time(status
, SOURCE_VOLTAGE
);
209 static int calculate_capacity(enum apm_source source
)
211 enum power_supply_property full_prop
, empty_prop
;
212 enum power_supply_property full_design_prop
, empty_design_prop
;
213 enum power_supply_property now_prop
, avg_prop
;
214 union power_supply_propval empty
, full
, cur
;
219 full_prop
= POWER_SUPPLY_PROP_CHARGE_FULL
;
220 empty_prop
= POWER_SUPPLY_PROP_CHARGE_EMPTY
;
221 full_design_prop
= POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN
;
222 empty_design_prop
= POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN
;
223 now_prop
= POWER_SUPPLY_PROP_CHARGE_NOW
;
224 avg_prop
= POWER_SUPPLY_PROP_CHARGE_AVG
;
227 full_prop
= POWER_SUPPLY_PROP_ENERGY_FULL
;
228 empty_prop
= POWER_SUPPLY_PROP_ENERGY_EMPTY
;
229 full_design_prop
= POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN
;
230 empty_design_prop
= POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN
;
231 now_prop
= POWER_SUPPLY_PROP_ENERGY_NOW
;
232 avg_prop
= POWER_SUPPLY_PROP_ENERGY_AVG
;
234 full_prop
= POWER_SUPPLY_PROP_VOLTAGE_MAX
;
235 empty_prop
= POWER_SUPPLY_PROP_VOLTAGE_MIN
;
236 full_design_prop
= POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN
;
237 empty_design_prop
= POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN
;
238 now_prop
= POWER_SUPPLY_PROP_VOLTAGE_NOW
;
239 avg_prop
= POWER_SUPPLY_PROP_VOLTAGE_AVG
;
242 printk(KERN_ERR
"Unsupported source: %d\n", source
);
246 if (_MPSY_PROP(full_prop
, &full
)) {
247 /* if battery can't report this property, use design value */
248 if (_MPSY_PROP(full_design_prop
, &full
))
252 if (_MPSY_PROP(avg_prop
, &cur
)) {
253 /* if battery can't report average value, use momentary */
254 if (_MPSY_PROP(now_prop
, &cur
))
258 if (_MPSY_PROP(empty_prop
, &empty
)) {
259 /* if battery can't report this property, use design value */
260 if (_MPSY_PROP(empty_design_prop
, &empty
))
264 if (full
.intval
- empty
.intval
)
265 ret
= ((cur
.intval
- empty
.intval
) * 100L) /
266 (full
.intval
- empty
.intval
);
278 static void apm_battery_apm_get_power_status(struct apm_power_info
*info
)
280 union power_supply_propval status
;
281 union power_supply_propval capacity
, time_to_full
, time_to_empty
;
283 mutex_lock(&apm_mutex
);
286 mutex_unlock(&apm_mutex
);
292 if (MPSY_PROP(STATUS
, &status
))
293 status
.intval
= POWER_SUPPLY_STATUS_UNKNOWN
;
297 if ((status
.intval
== POWER_SUPPLY_STATUS_CHARGING
) ||
298 (status
.intval
== POWER_SUPPLY_STATUS_NOT_CHARGING
) ||
299 (status
.intval
== POWER_SUPPLY_STATUS_FULL
))
300 info
->ac_line_status
= APM_AC_ONLINE
;
302 info
->ac_line_status
= APM_AC_OFFLINE
;
304 /* battery life (i.e. capacity, in percents) */
306 if (MPSY_PROP(CAPACITY
, &capacity
) == 0) {
307 info
->battery_life
= capacity
.intval
;
309 /* try calculate using energy */
310 info
->battery_life
= calculate_capacity(SOURCE_ENERGY
);
311 /* if failed try calculate using charge instead */
312 if (info
->battery_life
== -1)
313 info
->battery_life
= calculate_capacity(SOURCE_CHARGE
);
314 if (info
->battery_life
== -1)
315 info
->battery_life
= calculate_capacity(SOURCE_VOLTAGE
);
318 /* charging status */
320 if (status
.intval
== POWER_SUPPLY_STATUS_CHARGING
) {
321 info
->battery_status
= APM_BATTERY_STATUS_CHARGING
;
323 if (info
->battery_life
> 50)
324 info
->battery_status
= APM_BATTERY_STATUS_HIGH
;
325 else if (info
->battery_life
> 5)
326 info
->battery_status
= APM_BATTERY_STATUS_LOW
;
328 info
->battery_status
= APM_BATTERY_STATUS_CRITICAL
;
330 info
->battery_flag
= info
->battery_status
;
334 info
->units
= APM_UNITS_MINS
;
336 if (status
.intval
== POWER_SUPPLY_STATUS_CHARGING
) {
337 if (!MPSY_PROP(TIME_TO_FULL_AVG
, &time_to_full
) ||
338 !MPSY_PROP(TIME_TO_FULL_NOW
, &time_to_full
))
339 info
->time
= time_to_full
.intval
/ 60;
341 info
->time
= calculate_time(status
.intval
);
343 if (!MPSY_PROP(TIME_TO_EMPTY_AVG
, &time_to_empty
) ||
344 !MPSY_PROP(TIME_TO_EMPTY_NOW
, &time_to_empty
))
345 info
->time
= time_to_empty
.intval
/ 60;
347 info
->time
= calculate_time(status
.intval
);
350 mutex_unlock(&apm_mutex
);
353 static int __init
apm_battery_init(void)
355 printk(KERN_INFO
"APM Battery Driver\n");
357 apm_get_power_status
= apm_battery_apm_get_power_status
;
361 static void __exit
apm_battery_exit(void)
363 apm_get_power_status
= NULL
;
366 module_init(apm_battery_init
);
367 module_exit(apm_battery_exit
);
369 MODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>");
370 MODULE_DESCRIPTION("APM emulation driver for battery monitoring class");
371 MODULE_LICENSE("GPL");