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
, *batm
;
30 union power_supply_propval full
;
35 list_for_each_entry(dev
, &power_supply_class
->devices
, node
) {
36 bat
= dev_get_drvdata(dev
);
37 /* If none of battery devices cantains 'use_for_apm' flag,
38 choice one with maximum design charge */
39 if (!PSY_PROP(bat
, CHARGE_FULL_DESIGN
, &full
)) {
40 if (full
.intval
> max_charge
) {
42 max_charge
= full
.intval
;
55 static int calculate_time(int status
)
57 union power_supply_propval charge_full
, charge_empty
;
58 union power_supply_propval charge
, I
;
60 if (MPSY_PROP(CHARGE_FULL
, &charge_full
)) {
61 /* if battery can't report this property, use design value */
62 if (MPSY_PROP(CHARGE_FULL_DESIGN
, &charge_full
))
66 if (MPSY_PROP(CHARGE_EMPTY
, &charge_empty
)) {
67 /* if battery can't report this property, use design value */
68 if (MPSY_PROP(CHARGE_EMPTY_DESIGN
, &charge_empty
))
69 charge_empty
.intval
= 0;
72 if (MPSY_PROP(CHARGE_AVG
, &charge
)) {
73 /* if battery can't report average value, use momentary */
74 if (MPSY_PROP(CHARGE_NOW
, &charge
))
78 if (MPSY_PROP(CURRENT_AVG
, &I
)) {
79 /* if battery can't report average value, use momentary */
80 if (MPSY_PROP(CURRENT_NOW
, &I
))
84 if (status
== POWER_SUPPLY_STATUS_CHARGING
)
85 return ((charge
.intval
- charge_full
.intval
) * 60L) /
88 return -((charge
.intval
- charge_empty
.intval
) * 60L) /
92 static int calculate_capacity(int using_charge
)
94 enum power_supply_property full_prop
, empty_prop
;
95 enum power_supply_property full_design_prop
, empty_design_prop
;
96 enum power_supply_property now_prop
, avg_prop
;
97 union power_supply_propval empty
, full
, cur
;
101 full_prop
= POWER_SUPPLY_PROP_CHARGE_FULL
;
102 empty_prop
= POWER_SUPPLY_PROP_CHARGE_EMPTY
;
103 full_design_prop
= POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN
;
104 empty_design_prop
= POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN
;
105 now_prop
= POWER_SUPPLY_PROP_CHARGE_NOW
;
106 avg_prop
= POWER_SUPPLY_PROP_CHARGE_AVG
;
108 full_prop
= POWER_SUPPLY_PROP_ENERGY_FULL
;
109 empty_prop
= POWER_SUPPLY_PROP_ENERGY_EMPTY
;
110 full_design_prop
= POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN
;
111 empty_design_prop
= POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN
;
112 now_prop
= POWER_SUPPLY_PROP_ENERGY_NOW
;
113 avg_prop
= POWER_SUPPLY_PROP_ENERGY_AVG
;
116 if (_MPSY_PROP(full_prop
, &full
)) {
117 /* if battery can't report this property, use design value */
118 if (_MPSY_PROP(full_design_prop
, &full
))
122 if (_MPSY_PROP(avg_prop
, &cur
)) {
123 /* if battery can't report average value, use momentary */
124 if (_MPSY_PROP(now_prop
, &cur
))
128 if (_MPSY_PROP(empty_prop
, &empty
)) {
129 /* if battery can't report this property, use design value */
130 if (_MPSY_PROP(empty_design_prop
, &empty
))
134 if (full
.intval
- empty
.intval
)
135 ret
= ((cur
.intval
- empty
.intval
) * 100L) /
136 (full
.intval
- empty
.intval
);
148 static void apm_battery_apm_get_power_status(struct apm_power_info
*info
)
150 union power_supply_propval status
;
151 union power_supply_propval capacity
, time_to_full
, time_to_empty
;
153 down(&power_supply_class
->sem
);
156 up(&power_supply_class
->sem
);
162 if (MPSY_PROP(STATUS
, &status
))
163 status
.intval
= POWER_SUPPLY_STATUS_UNKNOWN
;
167 if ((status
.intval
== POWER_SUPPLY_STATUS_CHARGING
) ||
168 (status
.intval
== POWER_SUPPLY_STATUS_NOT_CHARGING
) ||
169 (status
.intval
== POWER_SUPPLY_STATUS_FULL
))
170 info
->ac_line_status
= APM_AC_ONLINE
;
172 info
->ac_line_status
= APM_AC_OFFLINE
;
174 /* battery life (i.e. capacity, in percents) */
176 if (MPSY_PROP(CAPACITY
, &capacity
) == 0) {
177 info
->battery_life
= capacity
.intval
;
179 /* try calculate using energy */
180 info
->battery_life
= calculate_capacity(0);
181 /* if failed try calculate using charge instead */
182 if (info
->battery_life
== -1)
183 info
->battery_life
= calculate_capacity(1);
186 /* charging status */
188 if (status
.intval
== POWER_SUPPLY_STATUS_CHARGING
) {
189 info
->battery_status
= APM_BATTERY_STATUS_CHARGING
;
191 if (info
->battery_life
> 50)
192 info
->battery_status
= APM_BATTERY_STATUS_HIGH
;
193 else if (info
->battery_life
> 5)
194 info
->battery_status
= APM_BATTERY_STATUS_LOW
;
196 info
->battery_status
= APM_BATTERY_STATUS_CRITICAL
;
198 info
->battery_flag
= info
->battery_status
;
202 info
->units
= APM_UNITS_MINS
;
204 if (status
.intval
== POWER_SUPPLY_STATUS_CHARGING
) {
205 if (MPSY_PROP(TIME_TO_FULL_AVG
, &time_to_full
)) {
206 if (MPSY_PROP(TIME_TO_FULL_NOW
, &time_to_full
))
207 info
->time
= calculate_time(status
.intval
);
209 info
->time
= time_to_full
.intval
/ 60;
212 if (MPSY_PROP(TIME_TO_EMPTY_AVG
, &time_to_empty
)) {
213 if (MPSY_PROP(TIME_TO_EMPTY_NOW
, &time_to_empty
))
214 info
->time
= calculate_time(status
.intval
);
216 info
->time
= time_to_empty
.intval
/ 60;
220 up(&power_supply_class
->sem
);
224 static int __init
apm_battery_init(void)
226 printk(KERN_INFO
"APM Battery Driver\n");
228 apm_get_power_status
= apm_battery_apm_get_power_status
;
232 static void __exit
apm_battery_exit(void)
234 apm_get_power_status
= NULL
;
238 module_init(apm_battery_init
);
239 module_exit(apm_battery_exit
);
241 MODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>");
242 MODULE_DESCRIPTION("APM emulation driver for battery monitoring class");
243 MODULE_LICENSE("GPL");