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/device.h>
14 #include <linux/power_supply.h>
15 #include <linux/apm-emulation.h>
18 #define PSY_PROP(psy, prop, val) (psy->get_property(psy, \
19 POWER_SUPPLY_PROP_##prop, val))
21 #define _MPSY_PROP(prop, val) (main_battery->get_property(main_battery, \
24 #define MPSY_PROP(prop, val) _MPSY_PROP(POWER_SUPPLY_PROP_##prop, val)
26 static DEFINE_MUTEX(apm_mutex
);
27 static struct power_supply
*main_battery
;
35 struct find_bat_param
{
36 struct power_supply
*main
;
37 struct power_supply
*bat
;
38 struct power_supply
*max_charge_bat
;
39 struct power_supply
*max_energy_bat
;
40 union power_supply_propval full
;
45 static int __find_main_battery(struct device
*dev
, void *data
)
47 struct find_bat_param
*bp
= (struct find_bat_param
*)data
;
49 bp
->bat
= dev_get_drvdata(dev
);
51 if (bp
->bat
->use_for_apm
) {
52 /* nice, we explicitly asked to report this battery. */
57 if (!PSY_PROP(bp
->bat
, CHARGE_FULL_DESIGN
, &bp
->full
) ||
58 !PSY_PROP(bp
->bat
, CHARGE_FULL
, &bp
->full
)) {
59 if (bp
->full
.intval
> bp
->max_charge
) {
60 bp
->max_charge_bat
= bp
->bat
;
61 bp
->max_charge
= bp
->full
.intval
;
63 } else if (!PSY_PROP(bp
->bat
, ENERGY_FULL_DESIGN
, &bp
->full
) ||
64 !PSY_PROP(bp
->bat
, ENERGY_FULL
, &bp
->full
)) {
65 if (bp
->full
.intval
> bp
->max_energy
) {
66 bp
->max_energy_bat
= bp
->bat
;
67 bp
->max_energy
= bp
->full
.intval
;
73 static void find_main_battery(void)
75 struct find_bat_param bp
;
78 memset(&bp
, 0, sizeof(struct find_bat_param
));
80 bp
.main
= main_battery
;
82 error
= class_for_each_device(power_supply_class
, NULL
, &bp
,
85 main_battery
= bp
.main
;
89 if ((bp
.max_energy_bat
&& bp
.max_charge_bat
) &&
90 (bp
.max_energy_bat
!= bp
.max_charge_bat
)) {
91 /* try guess battery with more capacity */
92 if (!PSY_PROP(bp
.max_charge_bat
, VOLTAGE_MAX_DESIGN
,
94 if (bp
.max_energy
> bp
.max_charge
* bp
.full
.intval
)
95 main_battery
= bp
.max_energy_bat
;
97 main_battery
= bp
.max_charge_bat
;
98 } else if (!PSY_PROP(bp
.max_energy_bat
, VOLTAGE_MAX_DESIGN
,
100 if (bp
.max_charge
> bp
.max_energy
/ bp
.full
.intval
)
101 main_battery
= bp
.max_charge_bat
;
103 main_battery
= bp
.max_energy_bat
;
105 /* give up, choice any */
106 main_battery
= bp
.max_energy_bat
;
108 } else if (bp
.max_charge_bat
) {
109 main_battery
= bp
.max_charge_bat
;
110 } else if (bp
.max_energy_bat
) {
111 main_battery
= bp
.max_energy_bat
;
113 /* give up, try the last if any */
114 main_battery
= bp
.bat
;
118 static int do_calculate_time(int status
, enum apm_source source
)
120 union power_supply_propval full
;
121 union power_supply_propval empty
;
122 union power_supply_propval cur
;
123 union power_supply_propval I
;
124 enum power_supply_property full_prop
;
125 enum power_supply_property full_design_prop
;
126 enum power_supply_property empty_prop
;
127 enum power_supply_property empty_design_prop
;
128 enum power_supply_property cur_avg_prop
;
129 enum power_supply_property cur_now_prop
;
131 if (MPSY_PROP(CURRENT_AVG
, &I
)) {
132 /* if battery can't report average value, use momentary */
133 if (MPSY_PROP(CURRENT_NOW
, &I
))
142 full_prop
= POWER_SUPPLY_PROP_CHARGE_FULL
;
143 full_design_prop
= POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN
;
144 empty_prop
= POWER_SUPPLY_PROP_CHARGE_EMPTY
;
145 empty_design_prop
= POWER_SUPPLY_PROP_CHARGE_EMPTY
;
146 cur_avg_prop
= POWER_SUPPLY_PROP_CHARGE_AVG
;
147 cur_now_prop
= POWER_SUPPLY_PROP_CHARGE_NOW
;
150 full_prop
= POWER_SUPPLY_PROP_ENERGY_FULL
;
151 full_design_prop
= POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN
;
152 empty_prop
= POWER_SUPPLY_PROP_ENERGY_EMPTY
;
153 empty_design_prop
= POWER_SUPPLY_PROP_CHARGE_EMPTY
;
154 cur_avg_prop
= POWER_SUPPLY_PROP_ENERGY_AVG
;
155 cur_now_prop
= POWER_SUPPLY_PROP_ENERGY_NOW
;
158 full_prop
= POWER_SUPPLY_PROP_VOLTAGE_MAX
;
159 full_design_prop
= POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN
;
160 empty_prop
= POWER_SUPPLY_PROP_VOLTAGE_MIN
;
161 empty_design_prop
= POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN
;
162 cur_avg_prop
= POWER_SUPPLY_PROP_VOLTAGE_AVG
;
163 cur_now_prop
= POWER_SUPPLY_PROP_VOLTAGE_NOW
;
166 printk(KERN_ERR
"Unsupported source: %d\n", source
);
170 if (_MPSY_PROP(full_prop
, &full
)) {
171 /* if battery can't report this property, use design value */
172 if (_MPSY_PROP(full_design_prop
, &full
))
176 if (_MPSY_PROP(empty_prop
, &empty
)) {
177 /* if battery can't report this property, use design value */
178 if (_MPSY_PROP(empty_design_prop
, &empty
))
182 if (_MPSY_PROP(cur_avg_prop
, &cur
)) {
183 /* if battery can't report average value, use momentary */
184 if (_MPSY_PROP(cur_now_prop
, &cur
))
188 if (status
== POWER_SUPPLY_STATUS_CHARGING
)
189 return ((cur
.intval
- full
.intval
) * 60L) / I
.intval
;
191 return -((cur
.intval
- empty
.intval
) * 60L) / I
.intval
;
194 static int calculate_time(int status
)
198 time
= do_calculate_time(status
, SOURCE_ENERGY
);
202 time
= do_calculate_time(status
, SOURCE_CHARGE
);
206 time
= do_calculate_time(status
, SOURCE_VOLTAGE
);
213 static int calculate_capacity(enum apm_source source
)
215 enum power_supply_property full_prop
, empty_prop
;
216 enum power_supply_property full_design_prop
, empty_design_prop
;
217 enum power_supply_property now_prop
, avg_prop
;
218 union power_supply_propval empty
, full
, cur
;
223 full_prop
= POWER_SUPPLY_PROP_CHARGE_FULL
;
224 empty_prop
= POWER_SUPPLY_PROP_CHARGE_EMPTY
;
225 full_design_prop
= POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN
;
226 empty_design_prop
= POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN
;
227 now_prop
= POWER_SUPPLY_PROP_CHARGE_NOW
;
228 avg_prop
= POWER_SUPPLY_PROP_CHARGE_AVG
;
231 full_prop
= POWER_SUPPLY_PROP_ENERGY_FULL
;
232 empty_prop
= POWER_SUPPLY_PROP_ENERGY_EMPTY
;
233 full_design_prop
= POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN
;
234 empty_design_prop
= POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN
;
235 now_prop
= POWER_SUPPLY_PROP_ENERGY_NOW
;
236 avg_prop
= POWER_SUPPLY_PROP_ENERGY_AVG
;
239 full_prop
= POWER_SUPPLY_PROP_VOLTAGE_MAX
;
240 empty_prop
= POWER_SUPPLY_PROP_VOLTAGE_MIN
;
241 full_design_prop
= POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN
;
242 empty_design_prop
= POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN
;
243 now_prop
= POWER_SUPPLY_PROP_VOLTAGE_NOW
;
244 avg_prop
= POWER_SUPPLY_PROP_VOLTAGE_AVG
;
247 printk(KERN_ERR
"Unsupported source: %d\n", source
);
251 if (_MPSY_PROP(full_prop
, &full
)) {
252 /* if battery can't report this property, use design value */
253 if (_MPSY_PROP(full_design_prop
, &full
))
257 if (_MPSY_PROP(avg_prop
, &cur
)) {
258 /* if battery can't report average value, use momentary */
259 if (_MPSY_PROP(now_prop
, &cur
))
263 if (_MPSY_PROP(empty_prop
, &empty
)) {
264 /* if battery can't report this property, use design value */
265 if (_MPSY_PROP(empty_design_prop
, &empty
))
269 if (full
.intval
- empty
.intval
)
270 ret
= ((cur
.intval
- empty
.intval
) * 100L) /
271 (full
.intval
- empty
.intval
);
283 static void apm_battery_apm_get_power_status(struct apm_power_info
*info
)
285 union power_supply_propval status
;
286 union power_supply_propval capacity
, time_to_full
, time_to_empty
;
288 mutex_lock(&apm_mutex
);
291 mutex_unlock(&apm_mutex
);
297 if (MPSY_PROP(STATUS
, &status
))
298 status
.intval
= POWER_SUPPLY_STATUS_UNKNOWN
;
302 if ((status
.intval
== POWER_SUPPLY_STATUS_CHARGING
) ||
303 (status
.intval
== POWER_SUPPLY_STATUS_NOT_CHARGING
) ||
304 (status
.intval
== POWER_SUPPLY_STATUS_FULL
))
305 info
->ac_line_status
= APM_AC_ONLINE
;
307 info
->ac_line_status
= APM_AC_OFFLINE
;
309 /* battery life (i.e. capacity, in percents) */
311 if (MPSY_PROP(CAPACITY
, &capacity
) == 0) {
312 info
->battery_life
= capacity
.intval
;
314 /* try calculate using energy */
315 info
->battery_life
= calculate_capacity(SOURCE_ENERGY
);
316 /* if failed try calculate using charge instead */
317 if (info
->battery_life
== -1)
318 info
->battery_life
= calculate_capacity(SOURCE_CHARGE
);
319 if (info
->battery_life
== -1)
320 info
->battery_life
= calculate_capacity(SOURCE_VOLTAGE
);
323 /* charging status */
325 if (status
.intval
== POWER_SUPPLY_STATUS_CHARGING
) {
326 info
->battery_status
= APM_BATTERY_STATUS_CHARGING
;
328 if (info
->battery_life
> 50)
329 info
->battery_status
= APM_BATTERY_STATUS_HIGH
;
330 else if (info
->battery_life
> 5)
331 info
->battery_status
= APM_BATTERY_STATUS_LOW
;
333 info
->battery_status
= APM_BATTERY_STATUS_CRITICAL
;
335 info
->battery_flag
= info
->battery_status
;
339 info
->units
= APM_UNITS_MINS
;
341 if (status
.intval
== POWER_SUPPLY_STATUS_CHARGING
) {
342 if (!MPSY_PROP(TIME_TO_FULL_AVG
, &time_to_full
) ||
343 !MPSY_PROP(TIME_TO_FULL_NOW
, &time_to_full
))
344 info
->time
= time_to_full
.intval
/ 60;
346 info
->time
= calculate_time(status
.intval
);
348 if (!MPSY_PROP(TIME_TO_EMPTY_AVG
, &time_to_empty
) ||
349 !MPSY_PROP(TIME_TO_EMPTY_NOW
, &time_to_empty
))
350 info
->time
= time_to_empty
.intval
/ 60;
352 info
->time
= calculate_time(status
.intval
);
355 mutex_unlock(&apm_mutex
);
358 static int __init
apm_battery_init(void)
360 printk(KERN_INFO
"APM Battery Driver\n");
362 apm_get_power_status
= apm_battery_apm_get_power_status
;
366 static void __exit
apm_battery_exit(void)
368 apm_get_power_status
= NULL
;
371 module_init(apm_battery_init
);
372 module_exit(apm_battery_exit
);
374 MODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>");
375 MODULE_DESCRIPTION("APM emulation driver for battery monitoring class");
376 MODULE_LICENSE("GPL");