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 #define PSY_PROP(psy, prop, val) psy->get_property(psy, \
17 POWER_SUPPLY_PROP_##prop, val)
19 #define _MPSY_PROP(prop, val) main_battery->get_property(main_battery, \
22 #define MPSY_PROP(prop, val) _MPSY_PROP(POWER_SUPPLY_PROP_##prop, val)
24 static struct power_supply
*main_battery
;
26 static void find_main_battery(void)
29 struct power_supply
*bat
= NULL
;
30 struct power_supply
*max_charge_bat
= NULL
;
31 struct power_supply
*max_energy_bat
= NULL
;
32 union power_supply_propval full
;
38 list_for_each_entry(dev
, &power_supply_class
->devices
, node
) {
39 bat
= dev_get_drvdata(dev
);
41 if (bat
->use_for_apm
) {
42 /* nice, we explicitly asked to report this battery. */
47 if (!PSY_PROP(bat
, CHARGE_FULL_DESIGN
, &full
) ||
48 !PSY_PROP(bat
, CHARGE_FULL
, &full
)) {
49 if (full
.intval
> max_charge
) {
51 max_charge
= full
.intval
;
53 } else if (!PSY_PROP(bat
, ENERGY_FULL_DESIGN
, &full
) ||
54 !PSY_PROP(bat
, ENERGY_FULL
, &full
)) {
55 if (full
.intval
> max_energy
) {
57 max_energy
= full
.intval
;
62 if ((max_energy_bat
&& max_charge_bat
) &&
63 (max_energy_bat
!= max_charge_bat
)) {
64 /* try guess battery with more capacity */
65 if (!PSY_PROP(max_charge_bat
, VOLTAGE_MAX_DESIGN
, &full
)) {
66 if (max_energy
> max_charge
* full
.intval
)
67 main_battery
= max_energy_bat
;
69 main_battery
= max_charge_bat
;
70 } else if (!PSY_PROP(max_energy_bat
, VOLTAGE_MAX_DESIGN
,
72 if (max_charge
> max_energy
/ full
.intval
)
73 main_battery
= max_charge_bat
;
75 main_battery
= max_energy_bat
;
77 /* give up, choice any */
78 main_battery
= max_energy_bat
;
80 } else if (max_charge_bat
) {
81 main_battery
= max_charge_bat
;
82 } else if (max_energy_bat
) {
83 main_battery
= max_energy_bat
;
85 /* give up, try the last if any */
90 static int calculate_time(int status
, int using_charge
)
92 union power_supply_propval full
;
93 union power_supply_propval empty
;
94 union power_supply_propval cur
;
95 union power_supply_propval I
;
96 enum power_supply_property full_prop
;
97 enum power_supply_property full_design_prop
;
98 enum power_supply_property empty_prop
;
99 enum power_supply_property empty_design_prop
;
100 enum power_supply_property cur_avg_prop
;
101 enum power_supply_property cur_now_prop
;
103 if (MPSY_PROP(CURRENT_AVG
, &I
)) {
104 /* if battery can't report average value, use momentary */
105 if (MPSY_PROP(CURRENT_NOW
, &I
))
110 full_prop
= POWER_SUPPLY_PROP_CHARGE_FULL
;
111 full_design_prop
= POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN
;
112 empty_prop
= POWER_SUPPLY_PROP_CHARGE_EMPTY
;
113 empty_design_prop
= POWER_SUPPLY_PROP_CHARGE_EMPTY
;
114 cur_avg_prop
= POWER_SUPPLY_PROP_CHARGE_AVG
;
115 cur_now_prop
= POWER_SUPPLY_PROP_CHARGE_NOW
;
117 full_prop
= POWER_SUPPLY_PROP_ENERGY_FULL
;
118 full_design_prop
= POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN
;
119 empty_prop
= POWER_SUPPLY_PROP_ENERGY_EMPTY
;
120 empty_design_prop
= POWER_SUPPLY_PROP_CHARGE_EMPTY
;
121 cur_avg_prop
= POWER_SUPPLY_PROP_ENERGY_AVG
;
122 cur_now_prop
= POWER_SUPPLY_PROP_ENERGY_NOW
;
125 if (_MPSY_PROP(full_prop
, &full
)) {
126 /* if battery can't report this property, use design value */
127 if (_MPSY_PROP(full_design_prop
, &full
))
131 if (_MPSY_PROP(empty_prop
, &empty
)) {
132 /* if battery can't report this property, use design value */
133 if (_MPSY_PROP(empty_design_prop
, &empty
))
137 if (_MPSY_PROP(cur_avg_prop
, &cur
)) {
138 /* if battery can't report average value, use momentary */
139 if (_MPSY_PROP(cur_now_prop
, &cur
))
143 if (status
== POWER_SUPPLY_STATUS_CHARGING
)
144 return ((cur
.intval
- full
.intval
) * 60L) / I
.intval
;
146 return -((cur
.intval
- empty
.intval
) * 60L) / I
.intval
;
149 static int calculate_capacity(int using_charge
)
151 enum power_supply_property full_prop
, empty_prop
;
152 enum power_supply_property full_design_prop
, empty_design_prop
;
153 enum power_supply_property now_prop
, avg_prop
;
154 union power_supply_propval empty
, full
, cur
;
158 full_prop
= POWER_SUPPLY_PROP_CHARGE_FULL
;
159 empty_prop
= POWER_SUPPLY_PROP_CHARGE_EMPTY
;
160 full_design_prop
= POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN
;
161 empty_design_prop
= POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN
;
162 now_prop
= POWER_SUPPLY_PROP_CHARGE_NOW
;
163 avg_prop
= POWER_SUPPLY_PROP_CHARGE_AVG
;
165 full_prop
= POWER_SUPPLY_PROP_ENERGY_FULL
;
166 empty_prop
= POWER_SUPPLY_PROP_ENERGY_EMPTY
;
167 full_design_prop
= POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN
;
168 empty_design_prop
= POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN
;
169 now_prop
= POWER_SUPPLY_PROP_ENERGY_NOW
;
170 avg_prop
= POWER_SUPPLY_PROP_ENERGY_AVG
;
173 if (_MPSY_PROP(full_prop
, &full
)) {
174 /* if battery can't report this property, use design value */
175 if (_MPSY_PROP(full_design_prop
, &full
))
179 if (_MPSY_PROP(avg_prop
, &cur
)) {
180 /* if battery can't report average value, use momentary */
181 if (_MPSY_PROP(now_prop
, &cur
))
185 if (_MPSY_PROP(empty_prop
, &empty
)) {
186 /* if battery can't report this property, use design value */
187 if (_MPSY_PROP(empty_design_prop
, &empty
))
191 if (full
.intval
- empty
.intval
)
192 ret
= ((cur
.intval
- empty
.intval
) * 100L) /
193 (full
.intval
- empty
.intval
);
205 static void apm_battery_apm_get_power_status(struct apm_power_info
*info
)
207 union power_supply_propval status
;
208 union power_supply_propval capacity
, time_to_full
, time_to_empty
;
210 down(&power_supply_class
->sem
);
213 up(&power_supply_class
->sem
);
219 if (MPSY_PROP(STATUS
, &status
))
220 status
.intval
= POWER_SUPPLY_STATUS_UNKNOWN
;
224 if ((status
.intval
== POWER_SUPPLY_STATUS_CHARGING
) ||
225 (status
.intval
== POWER_SUPPLY_STATUS_NOT_CHARGING
) ||
226 (status
.intval
== POWER_SUPPLY_STATUS_FULL
))
227 info
->ac_line_status
= APM_AC_ONLINE
;
229 info
->ac_line_status
= APM_AC_OFFLINE
;
231 /* battery life (i.e. capacity, in percents) */
233 if (MPSY_PROP(CAPACITY
, &capacity
) == 0) {
234 info
->battery_life
= capacity
.intval
;
236 /* try calculate using energy */
237 info
->battery_life
= calculate_capacity(0);
238 /* if failed try calculate using charge instead */
239 if (info
->battery_life
== -1)
240 info
->battery_life
= calculate_capacity(1);
243 /* charging status */
245 if (status
.intval
== POWER_SUPPLY_STATUS_CHARGING
) {
246 info
->battery_status
= APM_BATTERY_STATUS_CHARGING
;
248 if (info
->battery_life
> 50)
249 info
->battery_status
= APM_BATTERY_STATUS_HIGH
;
250 else if (info
->battery_life
> 5)
251 info
->battery_status
= APM_BATTERY_STATUS_LOW
;
253 info
->battery_status
= APM_BATTERY_STATUS_CRITICAL
;
255 info
->battery_flag
= info
->battery_status
;
259 info
->units
= APM_UNITS_MINS
;
261 if (status
.intval
== POWER_SUPPLY_STATUS_CHARGING
) {
262 if (!MPSY_PROP(TIME_TO_FULL_AVG
, &time_to_full
) ||
263 !MPSY_PROP(TIME_TO_FULL_NOW
, &time_to_full
)) {
264 info
->time
= time_to_full
.intval
/ 60;
266 info
->time
= calculate_time(status
.intval
, 0);
267 if (info
->time
== -1)
268 info
->time
= calculate_time(status
.intval
, 1);
271 if (!MPSY_PROP(TIME_TO_EMPTY_AVG
, &time_to_empty
) ||
272 !MPSY_PROP(TIME_TO_EMPTY_NOW
, &time_to_empty
)) {
273 info
->time
= time_to_empty
.intval
/ 60;
275 info
->time
= calculate_time(status
.intval
, 0);
276 if (info
->time
== -1)
277 info
->time
= calculate_time(status
.intval
, 1);
281 up(&power_supply_class
->sem
);
284 static int __init
apm_battery_init(void)
286 printk(KERN_INFO
"APM Battery Driver\n");
288 apm_get_power_status
= apm_battery_apm_get_power_status
;
292 static void __exit
apm_battery_exit(void)
294 apm_get_power_status
= NULL
;
297 module_init(apm_battery_init
);
298 module_exit(apm_battery_exit
);
300 MODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>");
301 MODULE_DESCRIPTION("APM emulation driver for battery monitoring class");
302 MODULE_LICENSE("GPL");