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
)
92 union power_supply_propval charge_full
, charge_empty
;
93 union power_supply_propval charge
, I
;
95 if (MPSY_PROP(CHARGE_FULL
, &charge_full
)) {
96 /* if battery can't report this property, use design value */
97 if (MPSY_PROP(CHARGE_FULL_DESIGN
, &charge_full
))
101 if (MPSY_PROP(CHARGE_EMPTY
, &charge_empty
)) {
102 /* if battery can't report this property, use design value */
103 if (MPSY_PROP(CHARGE_EMPTY_DESIGN
, &charge_empty
))
104 charge_empty
.intval
= 0;
107 if (MPSY_PROP(CHARGE_AVG
, &charge
)) {
108 /* if battery can't report average value, use momentary */
109 if (MPSY_PROP(CHARGE_NOW
, &charge
))
113 if (MPSY_PROP(CURRENT_AVG
, &I
)) {
114 /* if battery can't report average value, use momentary */
115 if (MPSY_PROP(CURRENT_NOW
, &I
))
119 if (status
== POWER_SUPPLY_STATUS_CHARGING
)
120 return ((charge
.intval
- charge_full
.intval
) * 60L) /
123 return -((charge
.intval
- charge_empty
.intval
) * 60L) /
127 static int calculate_capacity(int using_charge
)
129 enum power_supply_property full_prop
, empty_prop
;
130 enum power_supply_property full_design_prop
, empty_design_prop
;
131 enum power_supply_property now_prop
, avg_prop
;
132 union power_supply_propval empty
, full
, cur
;
136 full_prop
= POWER_SUPPLY_PROP_CHARGE_FULL
;
137 empty_prop
= POWER_SUPPLY_PROP_CHARGE_EMPTY
;
138 full_design_prop
= POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN
;
139 empty_design_prop
= POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN
;
140 now_prop
= POWER_SUPPLY_PROP_CHARGE_NOW
;
141 avg_prop
= POWER_SUPPLY_PROP_CHARGE_AVG
;
143 full_prop
= POWER_SUPPLY_PROP_ENERGY_FULL
;
144 empty_prop
= POWER_SUPPLY_PROP_ENERGY_EMPTY
;
145 full_design_prop
= POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN
;
146 empty_design_prop
= POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN
;
147 now_prop
= POWER_SUPPLY_PROP_ENERGY_NOW
;
148 avg_prop
= POWER_SUPPLY_PROP_ENERGY_AVG
;
151 if (_MPSY_PROP(full_prop
, &full
)) {
152 /* if battery can't report this property, use design value */
153 if (_MPSY_PROP(full_design_prop
, &full
))
157 if (_MPSY_PROP(avg_prop
, &cur
)) {
158 /* if battery can't report average value, use momentary */
159 if (_MPSY_PROP(now_prop
, &cur
))
163 if (_MPSY_PROP(empty_prop
, &empty
)) {
164 /* if battery can't report this property, use design value */
165 if (_MPSY_PROP(empty_design_prop
, &empty
))
169 if (full
.intval
- empty
.intval
)
170 ret
= ((cur
.intval
- empty
.intval
) * 100L) /
171 (full
.intval
- empty
.intval
);
183 static void apm_battery_apm_get_power_status(struct apm_power_info
*info
)
185 union power_supply_propval status
;
186 union power_supply_propval capacity
, time_to_full
, time_to_empty
;
188 down(&power_supply_class
->sem
);
191 up(&power_supply_class
->sem
);
197 if (MPSY_PROP(STATUS
, &status
))
198 status
.intval
= POWER_SUPPLY_STATUS_UNKNOWN
;
202 if ((status
.intval
== POWER_SUPPLY_STATUS_CHARGING
) ||
203 (status
.intval
== POWER_SUPPLY_STATUS_NOT_CHARGING
) ||
204 (status
.intval
== POWER_SUPPLY_STATUS_FULL
))
205 info
->ac_line_status
= APM_AC_ONLINE
;
207 info
->ac_line_status
= APM_AC_OFFLINE
;
209 /* battery life (i.e. capacity, in percents) */
211 if (MPSY_PROP(CAPACITY
, &capacity
) == 0) {
212 info
->battery_life
= capacity
.intval
;
214 /* try calculate using energy */
215 info
->battery_life
= calculate_capacity(0);
216 /* if failed try calculate using charge instead */
217 if (info
->battery_life
== -1)
218 info
->battery_life
= calculate_capacity(1);
221 /* charging status */
223 if (status
.intval
== POWER_SUPPLY_STATUS_CHARGING
) {
224 info
->battery_status
= APM_BATTERY_STATUS_CHARGING
;
226 if (info
->battery_life
> 50)
227 info
->battery_status
= APM_BATTERY_STATUS_HIGH
;
228 else if (info
->battery_life
> 5)
229 info
->battery_status
= APM_BATTERY_STATUS_LOW
;
231 info
->battery_status
= APM_BATTERY_STATUS_CRITICAL
;
233 info
->battery_flag
= info
->battery_status
;
237 info
->units
= APM_UNITS_MINS
;
239 if (status
.intval
== POWER_SUPPLY_STATUS_CHARGING
) {
240 if (!MPSY_PROP(TIME_TO_FULL_AVG
, &time_to_full
) ||
241 !MPSY_PROP(TIME_TO_FULL_NOW
, &time_to_full
))
242 info
->time
= time_to_full
.intval
/ 60;
244 info
->time
= calculate_time(status
.intval
);
246 if (!MPSY_PROP(TIME_TO_EMPTY_AVG
, &time_to_empty
) ||
247 !MPSY_PROP(TIME_TO_EMPTY_NOW
, &time_to_empty
))
248 info
->time
= time_to_empty
.intval
/ 60;
250 info
->time
= calculate_time(status
.intval
);
253 up(&power_supply_class
->sem
);
256 static int __init
apm_battery_init(void)
258 printk(KERN_INFO
"APM Battery Driver\n");
260 apm_get_power_status
= apm_battery_apm_get_power_status
;
264 static void __exit
apm_battery_exit(void)
266 apm_get_power_status
= NULL
;
269 module_init(apm_battery_init
);
270 module_exit(apm_battery_exit
);
272 MODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>");
273 MODULE_DESCRIPTION("APM emulation driver for battery monitoring class");
274 MODULE_LICENSE("GPL");