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
;
53 static int calculate_time(int status
)
55 union power_supply_propval charge_full
, charge_empty
;
56 union power_supply_propval charge
, I
;
58 if (MPSY_PROP(CHARGE_FULL
, &charge_full
)) {
59 /* if battery can't report this property, use design value */
60 if (MPSY_PROP(CHARGE_FULL_DESIGN
, &charge_full
))
64 if (MPSY_PROP(CHARGE_EMPTY
, &charge_empty
)) {
65 /* if battery can't report this property, use design value */
66 if (MPSY_PROP(CHARGE_EMPTY_DESIGN
, &charge_empty
))
67 charge_empty
.intval
= 0;
70 if (MPSY_PROP(CHARGE_AVG
, &charge
)) {
71 /* if battery can't report average value, use momentary */
72 if (MPSY_PROP(CHARGE_NOW
, &charge
))
76 if (MPSY_PROP(CURRENT_AVG
, &I
)) {
77 /* if battery can't report average value, use momentary */
78 if (MPSY_PROP(CURRENT_NOW
, &I
))
82 if (status
== POWER_SUPPLY_STATUS_CHARGING
)
83 return ((charge
.intval
- charge_full
.intval
) * 60L) /
86 return -((charge
.intval
- charge_empty
.intval
) * 60L) /
90 static int calculate_capacity(int using_charge
)
92 enum power_supply_property full_prop
, empty_prop
;
93 enum power_supply_property full_design_prop
, empty_design_prop
;
94 enum power_supply_property now_prop
, avg_prop
;
95 union power_supply_propval empty
, full
, cur
;
99 full_prop
= POWER_SUPPLY_PROP_CHARGE_FULL
;
100 empty_prop
= POWER_SUPPLY_PROP_CHARGE_EMPTY
;
101 full_design_prop
= POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN
;
102 empty_design_prop
= POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN
;
103 now_prop
= POWER_SUPPLY_PROP_CHARGE_NOW
;
104 avg_prop
= POWER_SUPPLY_PROP_CHARGE_AVG
;
106 full_prop
= POWER_SUPPLY_PROP_ENERGY_FULL
;
107 empty_prop
= POWER_SUPPLY_PROP_ENERGY_EMPTY
;
108 full_design_prop
= POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN
;
109 empty_design_prop
= POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN
;
110 now_prop
= POWER_SUPPLY_PROP_ENERGY_NOW
;
111 avg_prop
= POWER_SUPPLY_PROP_ENERGY_AVG
;
114 if (_MPSY_PROP(full_prop
, &full
)) {
115 /* if battery can't report this property, use design value */
116 if (_MPSY_PROP(full_design_prop
, &full
))
120 if (_MPSY_PROP(avg_prop
, &cur
)) {
121 /* if battery can't report average value, use momentary */
122 if (_MPSY_PROP(now_prop
, &cur
))
126 if (_MPSY_PROP(empty_prop
, &empty
)) {
127 /* if battery can't report this property, use design value */
128 if (_MPSY_PROP(empty_design_prop
, &empty
))
132 if (full
.intval
- empty
.intval
)
133 ret
= ((cur
.intval
- empty
.intval
) * 100L) /
134 (full
.intval
- empty
.intval
);
146 static void apm_battery_apm_get_power_status(struct apm_power_info
*info
)
148 union power_supply_propval status
;
149 union power_supply_propval capacity
, time_to_full
, time_to_empty
;
151 down(&power_supply_class
->sem
);
154 up(&power_supply_class
->sem
);
160 if (MPSY_PROP(STATUS
, &status
))
161 status
.intval
= POWER_SUPPLY_STATUS_UNKNOWN
;
165 if ((status
.intval
== POWER_SUPPLY_STATUS_CHARGING
) ||
166 (status
.intval
== POWER_SUPPLY_STATUS_NOT_CHARGING
) ||
167 (status
.intval
== POWER_SUPPLY_STATUS_FULL
))
168 info
->ac_line_status
= APM_AC_ONLINE
;
170 info
->ac_line_status
= APM_AC_OFFLINE
;
172 /* battery life (i.e. capacity, in percents) */
174 if (MPSY_PROP(CAPACITY
, &capacity
) == 0) {
175 info
->battery_life
= capacity
.intval
;
177 /* try calculate using energy */
178 info
->battery_life
= calculate_capacity(0);
179 /* if failed try calculate using charge instead */
180 if (info
->battery_life
== -1)
181 info
->battery_life
= calculate_capacity(1);
184 /* charging status */
186 if (status
.intval
== POWER_SUPPLY_STATUS_CHARGING
) {
187 info
->battery_status
= APM_BATTERY_STATUS_CHARGING
;
189 if (info
->battery_life
> 50)
190 info
->battery_status
= APM_BATTERY_STATUS_HIGH
;
191 else if (info
->battery_life
> 5)
192 info
->battery_status
= APM_BATTERY_STATUS_LOW
;
194 info
->battery_status
= APM_BATTERY_STATUS_CRITICAL
;
196 info
->battery_flag
= info
->battery_status
;
200 info
->units
= APM_UNITS_MINS
;
202 if (status
.intval
== POWER_SUPPLY_STATUS_CHARGING
) {
203 if (MPSY_PROP(TIME_TO_FULL_AVG
, &time_to_full
)) {
204 if (MPSY_PROP(TIME_TO_FULL_NOW
, &time_to_full
))
205 info
->time
= calculate_time(status
.intval
);
207 info
->time
= time_to_full
.intval
/ 60;
210 if (MPSY_PROP(TIME_TO_EMPTY_AVG
, &time_to_empty
)) {
211 if (MPSY_PROP(TIME_TO_EMPTY_NOW
, &time_to_empty
))
212 info
->time
= calculate_time(status
.intval
);
214 info
->time
= time_to_empty
.intval
/ 60;
218 up(&power_supply_class
->sem
);
221 static int __init
apm_battery_init(void)
223 printk(KERN_INFO
"APM Battery Driver\n");
225 apm_get_power_status
= apm_battery_apm_get_power_status
;
229 static void __exit
apm_battery_exit(void)
231 apm_get_power_status
= NULL
;
234 module_init(apm_battery_init
);
235 module_exit(apm_battery_exit
);
237 MODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>");
238 MODULE_DESCRIPTION("APM emulation driver for battery monitoring class");
239 MODULE_LICENSE("GPL");