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
, NULL
, &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
))
141 full_prop
= POWER_SUPPLY_PROP_CHARGE_FULL
;
142 full_design_prop
= POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN
;
143 empty_prop
= POWER_SUPPLY_PROP_CHARGE_EMPTY
;
144 empty_design_prop
= POWER_SUPPLY_PROP_CHARGE_EMPTY
;
145 cur_avg_prop
= POWER_SUPPLY_PROP_CHARGE_AVG
;
146 cur_now_prop
= POWER_SUPPLY_PROP_CHARGE_NOW
;
149 full_prop
= POWER_SUPPLY_PROP_ENERGY_FULL
;
150 full_design_prop
= POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN
;
151 empty_prop
= POWER_SUPPLY_PROP_ENERGY_EMPTY
;
152 empty_design_prop
= POWER_SUPPLY_PROP_CHARGE_EMPTY
;
153 cur_avg_prop
= POWER_SUPPLY_PROP_ENERGY_AVG
;
154 cur_now_prop
= POWER_SUPPLY_PROP_ENERGY_NOW
;
157 full_prop
= POWER_SUPPLY_PROP_VOLTAGE_MAX
;
158 full_design_prop
= POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN
;
159 empty_prop
= POWER_SUPPLY_PROP_VOLTAGE_MIN
;
160 empty_design_prop
= POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN
;
161 cur_avg_prop
= POWER_SUPPLY_PROP_VOLTAGE_AVG
;
162 cur_now_prop
= POWER_SUPPLY_PROP_VOLTAGE_NOW
;
165 printk(KERN_ERR
"Unsupported source: %d\n", source
);
169 if (_MPSY_PROP(full_prop
, &full
)) {
170 /* if battery can't report this property, use design value */
171 if (_MPSY_PROP(full_design_prop
, &full
))
175 if (_MPSY_PROP(empty_prop
, &empty
)) {
176 /* if battery can't report this property, use design value */
177 if (_MPSY_PROP(empty_design_prop
, &empty
))
181 if (_MPSY_PROP(cur_avg_prop
, &cur
)) {
182 /* if battery can't report average value, use momentary */
183 if (_MPSY_PROP(cur_now_prop
, &cur
))
187 if (status
== POWER_SUPPLY_STATUS_CHARGING
)
188 return ((cur
.intval
- full
.intval
) * 60L) / I
.intval
;
190 return -((cur
.intval
- empty
.intval
) * 60L) / I
.intval
;
193 static int calculate_time(int status
)
197 time
= do_calculate_time(status
, SOURCE_ENERGY
);
201 time
= do_calculate_time(status
, SOURCE_CHARGE
);
205 time
= do_calculate_time(status
, SOURCE_VOLTAGE
);
212 static int calculate_capacity(enum apm_source source
)
214 enum power_supply_property full_prop
, empty_prop
;
215 enum power_supply_property full_design_prop
, empty_design_prop
;
216 enum power_supply_property now_prop
, avg_prop
;
217 union power_supply_propval empty
, full
, cur
;
222 full_prop
= POWER_SUPPLY_PROP_CHARGE_FULL
;
223 empty_prop
= POWER_SUPPLY_PROP_CHARGE_EMPTY
;
224 full_design_prop
= POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN
;
225 empty_design_prop
= POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN
;
226 now_prop
= POWER_SUPPLY_PROP_CHARGE_NOW
;
227 avg_prop
= POWER_SUPPLY_PROP_CHARGE_AVG
;
230 full_prop
= POWER_SUPPLY_PROP_ENERGY_FULL
;
231 empty_prop
= POWER_SUPPLY_PROP_ENERGY_EMPTY
;
232 full_design_prop
= POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN
;
233 empty_design_prop
= POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN
;
234 now_prop
= POWER_SUPPLY_PROP_ENERGY_NOW
;
235 avg_prop
= POWER_SUPPLY_PROP_ENERGY_AVG
;
238 full_prop
= POWER_SUPPLY_PROP_VOLTAGE_MAX
;
239 empty_prop
= POWER_SUPPLY_PROP_VOLTAGE_MIN
;
240 full_design_prop
= POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN
;
241 empty_design_prop
= POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN
;
242 now_prop
= POWER_SUPPLY_PROP_VOLTAGE_NOW
;
243 avg_prop
= POWER_SUPPLY_PROP_VOLTAGE_AVG
;
246 printk(KERN_ERR
"Unsupported source: %d\n", source
);
250 if (_MPSY_PROP(full_prop
, &full
)) {
251 /* if battery can't report this property, use design value */
252 if (_MPSY_PROP(full_design_prop
, &full
))
256 if (_MPSY_PROP(avg_prop
, &cur
)) {
257 /* if battery can't report average value, use momentary */
258 if (_MPSY_PROP(now_prop
, &cur
))
262 if (_MPSY_PROP(empty_prop
, &empty
)) {
263 /* if battery can't report this property, use design value */
264 if (_MPSY_PROP(empty_design_prop
, &empty
))
268 if (full
.intval
- empty
.intval
)
269 ret
= ((cur
.intval
- empty
.intval
) * 100L) /
270 (full
.intval
- empty
.intval
);
282 static void apm_battery_apm_get_power_status(struct apm_power_info
*info
)
284 union power_supply_propval status
;
285 union power_supply_propval capacity
, time_to_full
, time_to_empty
;
287 mutex_lock(&apm_mutex
);
290 mutex_unlock(&apm_mutex
);
296 if (MPSY_PROP(STATUS
, &status
))
297 status
.intval
= POWER_SUPPLY_STATUS_UNKNOWN
;
301 if ((status
.intval
== POWER_SUPPLY_STATUS_CHARGING
) ||
302 (status
.intval
== POWER_SUPPLY_STATUS_NOT_CHARGING
) ||
303 (status
.intval
== POWER_SUPPLY_STATUS_FULL
))
304 info
->ac_line_status
= APM_AC_ONLINE
;
306 info
->ac_line_status
= APM_AC_OFFLINE
;
308 /* battery life (i.e. capacity, in percents) */
310 if (MPSY_PROP(CAPACITY
, &capacity
) == 0) {
311 info
->battery_life
= capacity
.intval
;
313 /* try calculate using energy */
314 info
->battery_life
= calculate_capacity(SOURCE_ENERGY
);
315 /* if failed try calculate using charge instead */
316 if (info
->battery_life
== -1)
317 info
->battery_life
= calculate_capacity(SOURCE_CHARGE
);
318 if (info
->battery_life
== -1)
319 info
->battery_life
= calculate_capacity(SOURCE_VOLTAGE
);
322 /* charging status */
324 if (status
.intval
== POWER_SUPPLY_STATUS_CHARGING
) {
325 info
->battery_status
= APM_BATTERY_STATUS_CHARGING
;
327 if (info
->battery_life
> 50)
328 info
->battery_status
= APM_BATTERY_STATUS_HIGH
;
329 else if (info
->battery_life
> 5)
330 info
->battery_status
= APM_BATTERY_STATUS_LOW
;
332 info
->battery_status
= APM_BATTERY_STATUS_CRITICAL
;
334 info
->battery_flag
= info
->battery_status
;
338 info
->units
= APM_UNITS_MINS
;
340 if (status
.intval
== POWER_SUPPLY_STATUS_CHARGING
) {
341 if (!MPSY_PROP(TIME_TO_FULL_AVG
, &time_to_full
) ||
342 !MPSY_PROP(TIME_TO_FULL_NOW
, &time_to_full
))
343 info
->time
= time_to_full
.intval
/ 60;
345 info
->time
= calculate_time(status
.intval
);
347 if (!MPSY_PROP(TIME_TO_EMPTY_AVG
, &time_to_empty
) ||
348 !MPSY_PROP(TIME_TO_EMPTY_NOW
, &time_to_empty
))
349 info
->time
= time_to_empty
.intval
/ 60;
351 info
->time
= calculate_time(status
.intval
);
354 mutex_unlock(&apm_mutex
);
357 static int __init
apm_battery_init(void)
359 printk(KERN_INFO
"APM Battery Driver\n");
361 apm_get_power_status
= apm_battery_apm_get_power_status
;
365 static void __exit
apm_battery_exit(void)
367 apm_get_power_status
= NULL
;
370 module_init(apm_battery_init
);
371 module_exit(apm_battery_exit
);
373 MODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>");
374 MODULE_DESCRIPTION("APM emulation driver for battery monitoring class");
375 MODULE_LICENSE("GPL");