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 static DEFINE_MUTEX(apm_mutex
);
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 struct power_supply
*main_battery
;
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
;
37 static int __find_main_battery(struct device
*dev
, void *data
)
39 struct find_bat_param
*bp
= (struct find_bat_param
*)data
;
41 bp
->bat
= dev_get_drvdata(dev
);
43 if (bp
->bat
->use_for_apm
) {
44 /* nice, we explicitly asked to report this battery. */
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
;
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
;
65 static void find_main_battery(void)
67 struct find_bat_param bp
;
70 memset(&bp
, 0, sizeof(struct find_bat_param
));
72 bp
.main
= main_battery
;
74 error
= class_for_each_device(power_supply_class
, &bp
,
77 main_battery
= bp
.main
;
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
,
86 if (bp
.max_energy
> bp
.max_charge
* bp
.full
.intval
)
87 main_battery
= bp
.max_energy_bat
;
89 main_battery
= bp
.max_charge_bat
;
90 } else if (!PSY_PROP(bp
.max_energy_bat
, VOLTAGE_MAX_DESIGN
,
92 if (bp
.max_charge
> bp
.max_energy
/ bp
.full
.intval
)
93 main_battery
= bp
.max_charge_bat
;
95 main_battery
= bp
.max_energy_bat
;
97 /* give up, choice any */
98 main_battery
= bp
.max_energy_bat
;
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
;
105 /* give up, try the last if any */
106 main_battery
= bp
.bat
;
110 static int calculate_time(int status
, int using_charge
)
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
;
123 if (MPSY_PROP(CURRENT_AVG
, &I
)) {
124 /* if battery can't report average value, use momentary */
125 if (MPSY_PROP(CURRENT_NOW
, &I
))
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
;
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
;
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
))
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
))
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
))
163 if (status
== POWER_SUPPLY_STATUS_CHARGING
)
164 return ((cur
.intval
- full
.intval
) * 60L) / I
.intval
;
166 return -((cur
.intval
- empty
.intval
) * 60L) / I
.intval
;
169 static int calculate_capacity(int using_charge
)
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
;
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
;
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
;
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
))
199 if (_MPSY_PROP(avg_prop
, &cur
)) {
200 /* if battery can't report average value, use momentary */
201 if (_MPSY_PROP(now_prop
, &cur
))
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
))
211 if (full
.intval
- empty
.intval
)
212 ret
= ((cur
.intval
- empty
.intval
) * 100L) /
213 (full
.intval
- empty
.intval
);
225 static void apm_battery_apm_get_power_status(struct apm_power_info
*info
)
227 union power_supply_propval status
;
228 union power_supply_propval capacity
, time_to_full
, time_to_empty
;
230 mutex_lock(&apm_mutex
);
233 mutex_unlock(&apm_mutex
);
239 if (MPSY_PROP(STATUS
, &status
))
240 status
.intval
= POWER_SUPPLY_STATUS_UNKNOWN
;
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
;
249 info
->ac_line_status
= APM_AC_OFFLINE
;
251 /* battery life (i.e. capacity, in percents) */
253 if (MPSY_PROP(CAPACITY
, &capacity
) == 0) {
254 info
->battery_life
= capacity
.intval
;
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);
263 /* charging status */
265 if (status
.intval
== POWER_SUPPLY_STATUS_CHARGING
) {
266 info
->battery_status
= APM_BATTERY_STATUS_CHARGING
;
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
;
273 info
->battery_status
= APM_BATTERY_STATUS_CRITICAL
;
275 info
->battery_flag
= info
->battery_status
;
279 info
->units
= APM_UNITS_MINS
;
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;
286 info
->time
= calculate_time(status
.intval
, 0);
287 if (info
->time
== -1)
288 info
->time
= calculate_time(status
.intval
, 1);
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;
295 info
->time
= calculate_time(status
.intval
, 0);
296 if (info
->time
== -1)
297 info
->time
= calculate_time(status
.intval
, 1);
301 mutex_unlock(&apm_mutex
);
304 static int __init
apm_battery_init(void)
306 printk(KERN_INFO
"APM Battery Driver\n");
308 apm_get_power_status
= apm_battery_apm_get_power_status
;
312 static void __exit
apm_battery_exit(void)
314 apm_get_power_status
= NULL
;
317 module_init(apm_battery_init
);
318 module_exit(apm_battery_exit
);
320 MODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>");
321 MODULE_DESCRIPTION("APM emulation driver for battery monitoring class");
322 MODULE_LICENSE("GPL");