0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 #include <linux/module.h>
0013 #include <linux/device.h>
0014 #include <linux/power_supply.h>
0015 #include <linux/apm-emulation.h>
0016
0017
0018 #define PSY_PROP(psy, prop, val) (power_supply_get_property(psy, \
0019 POWER_SUPPLY_PROP_##prop, val))
0020
0021 #define _MPSY_PROP(prop, val) (power_supply_get_property(main_battery, \
0022 prop, val))
0023
0024 #define MPSY_PROP(prop, val) _MPSY_PROP(POWER_SUPPLY_PROP_##prop, val)
0025
0026 static DEFINE_MUTEX(apm_mutex);
0027 static struct power_supply *main_battery;
0028
0029 enum apm_source {
0030 SOURCE_ENERGY,
0031 SOURCE_CHARGE,
0032 SOURCE_VOLTAGE,
0033 };
0034
0035 struct find_bat_param {
0036 struct power_supply *main;
0037 struct power_supply *bat;
0038 struct power_supply *max_charge_bat;
0039 struct power_supply *max_energy_bat;
0040 union power_supply_propval full;
0041 int max_charge;
0042 int max_energy;
0043 };
0044
0045 static int __find_main_battery(struct device *dev, void *data)
0046 {
0047 struct find_bat_param *bp = (struct find_bat_param *)data;
0048
0049 bp->bat = dev_get_drvdata(dev);
0050
0051 if (bp->bat->desc->use_for_apm) {
0052
0053 bp->main = bp->bat;
0054 return 1;
0055 }
0056
0057 if (!PSY_PROP(bp->bat, CHARGE_FULL_DESIGN, &bp->full) ||
0058 !PSY_PROP(bp->bat, CHARGE_FULL, &bp->full)) {
0059 if (bp->full.intval > bp->max_charge) {
0060 bp->max_charge_bat = bp->bat;
0061 bp->max_charge = bp->full.intval;
0062 }
0063 } else if (!PSY_PROP(bp->bat, ENERGY_FULL_DESIGN, &bp->full) ||
0064 !PSY_PROP(bp->bat, ENERGY_FULL, &bp->full)) {
0065 if (bp->full.intval > bp->max_energy) {
0066 bp->max_energy_bat = bp->bat;
0067 bp->max_energy = bp->full.intval;
0068 }
0069 }
0070 return 0;
0071 }
0072
0073 static void find_main_battery(void)
0074 {
0075 struct find_bat_param bp;
0076 int error;
0077
0078 memset(&bp, 0, sizeof(struct find_bat_param));
0079 main_battery = NULL;
0080 bp.main = main_battery;
0081
0082 error = class_for_each_device(power_supply_class, NULL, &bp,
0083 __find_main_battery);
0084 if (error) {
0085 main_battery = bp.main;
0086 return;
0087 }
0088
0089 if ((bp.max_energy_bat && bp.max_charge_bat) &&
0090 (bp.max_energy_bat != bp.max_charge_bat)) {
0091
0092 if (!PSY_PROP(bp.max_charge_bat, VOLTAGE_MAX_DESIGN,
0093 &bp.full)) {
0094 if (bp.max_energy > bp.max_charge * bp.full.intval)
0095 main_battery = bp.max_energy_bat;
0096 else
0097 main_battery = bp.max_charge_bat;
0098 } else if (!PSY_PROP(bp.max_energy_bat, VOLTAGE_MAX_DESIGN,
0099 &bp.full)) {
0100 if (bp.max_charge > bp.max_energy / bp.full.intval)
0101 main_battery = bp.max_charge_bat;
0102 else
0103 main_battery = bp.max_energy_bat;
0104 } else {
0105
0106 main_battery = bp.max_energy_bat;
0107 }
0108 } else if (bp.max_charge_bat) {
0109 main_battery = bp.max_charge_bat;
0110 } else if (bp.max_energy_bat) {
0111 main_battery = bp.max_energy_bat;
0112 } else {
0113
0114 main_battery = bp.bat;
0115 }
0116 }
0117
0118 static int do_calculate_time(int status, enum apm_source source)
0119 {
0120 union power_supply_propval full;
0121 union power_supply_propval empty;
0122 union power_supply_propval cur;
0123 union power_supply_propval I;
0124 enum power_supply_property full_prop;
0125 enum power_supply_property full_design_prop;
0126 enum power_supply_property empty_prop;
0127 enum power_supply_property empty_design_prop;
0128 enum power_supply_property cur_avg_prop;
0129 enum power_supply_property cur_now_prop;
0130
0131 if (MPSY_PROP(CURRENT_AVG, &I)) {
0132
0133 if (MPSY_PROP(CURRENT_NOW, &I))
0134 return -1;
0135 }
0136
0137 if (!I.intval)
0138 return 0;
0139
0140 switch (source) {
0141 case SOURCE_CHARGE:
0142 full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
0143 full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
0144 empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
0145 empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
0146 cur_avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
0147 cur_now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
0148 break;
0149 case SOURCE_ENERGY:
0150 full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
0151 full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
0152 empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
0153 empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
0154 cur_avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
0155 cur_now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
0156 break;
0157 case SOURCE_VOLTAGE:
0158 full_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX;
0159 full_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
0160 empty_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN;
0161 empty_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
0162 cur_avg_prop = POWER_SUPPLY_PROP_VOLTAGE_AVG;
0163 cur_now_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
0164 break;
0165 default:
0166 printk(KERN_ERR "Unsupported source: %d\n", source);
0167 return -1;
0168 }
0169
0170 if (_MPSY_PROP(full_prop, &full)) {
0171
0172 if (_MPSY_PROP(full_design_prop, &full))
0173 return -1;
0174 }
0175
0176 if (_MPSY_PROP(empty_prop, &empty)) {
0177
0178 if (_MPSY_PROP(empty_design_prop, &empty))
0179 empty.intval = 0;
0180 }
0181
0182 if (_MPSY_PROP(cur_avg_prop, &cur)) {
0183
0184 if (_MPSY_PROP(cur_now_prop, &cur))
0185 return -1;
0186 }
0187
0188 if (status == POWER_SUPPLY_STATUS_CHARGING)
0189 return ((cur.intval - full.intval) * 60L) / I.intval;
0190 else
0191 return -((cur.intval - empty.intval) * 60L) / I.intval;
0192 }
0193
0194 static int calculate_time(int status)
0195 {
0196 int time;
0197
0198 time = do_calculate_time(status, SOURCE_ENERGY);
0199 if (time != -1)
0200 return time;
0201
0202 time = do_calculate_time(status, SOURCE_CHARGE);
0203 if (time != -1)
0204 return time;
0205
0206 time = do_calculate_time(status, SOURCE_VOLTAGE);
0207 if (time != -1)
0208 return time;
0209
0210 return -1;
0211 }
0212
0213 static int calculate_capacity(enum apm_source source)
0214 {
0215 enum power_supply_property full_prop, empty_prop;
0216 enum power_supply_property full_design_prop, empty_design_prop;
0217 enum power_supply_property now_prop, avg_prop;
0218 union power_supply_propval empty, full, cur;
0219 int ret;
0220
0221 switch (source) {
0222 case SOURCE_CHARGE:
0223 full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
0224 empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
0225 full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
0226 empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN;
0227 now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
0228 avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
0229 break;
0230 case SOURCE_ENERGY:
0231 full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
0232 empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
0233 full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
0234 empty_design_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN;
0235 now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
0236 avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
0237 break;
0238 case SOURCE_VOLTAGE:
0239 full_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX;
0240 empty_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN;
0241 full_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
0242 empty_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
0243 now_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
0244 avg_prop = POWER_SUPPLY_PROP_VOLTAGE_AVG;
0245 break;
0246 default:
0247 printk(KERN_ERR "Unsupported source: %d\n", source);
0248 return -1;
0249 }
0250
0251 if (_MPSY_PROP(full_prop, &full)) {
0252
0253 if (_MPSY_PROP(full_design_prop, &full))
0254 return -1;
0255 }
0256
0257 if (_MPSY_PROP(avg_prop, &cur)) {
0258
0259 if (_MPSY_PROP(now_prop, &cur))
0260 return -1;
0261 }
0262
0263 if (_MPSY_PROP(empty_prop, &empty)) {
0264
0265 if (_MPSY_PROP(empty_design_prop, &empty))
0266 empty.intval = 0;
0267 }
0268
0269 if (full.intval - empty.intval)
0270 ret = ((cur.intval - empty.intval) * 100L) /
0271 (full.intval - empty.intval);
0272 else
0273 return -1;
0274
0275 if (ret > 100)
0276 return 100;
0277 else if (ret < 0)
0278 return 0;
0279
0280 return ret;
0281 }
0282
0283 static void apm_battery_apm_get_power_status(struct apm_power_info *info)
0284 {
0285 union power_supply_propval status;
0286 union power_supply_propval capacity, time_to_full, time_to_empty;
0287
0288 mutex_lock(&apm_mutex);
0289 find_main_battery();
0290 if (!main_battery) {
0291 mutex_unlock(&apm_mutex);
0292 return;
0293 }
0294
0295
0296
0297 if (MPSY_PROP(STATUS, &status))
0298 status.intval = POWER_SUPPLY_STATUS_UNKNOWN;
0299
0300
0301
0302 if ((status.intval == POWER_SUPPLY_STATUS_CHARGING) ||
0303 (status.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) ||
0304 (status.intval == POWER_SUPPLY_STATUS_FULL))
0305 info->ac_line_status = APM_AC_ONLINE;
0306 else
0307 info->ac_line_status = APM_AC_OFFLINE;
0308
0309
0310
0311 if (MPSY_PROP(CAPACITY, &capacity) == 0) {
0312 info->battery_life = capacity.intval;
0313 } else {
0314
0315 info->battery_life = calculate_capacity(SOURCE_ENERGY);
0316
0317 if (info->battery_life == -1)
0318 info->battery_life = calculate_capacity(SOURCE_CHARGE);
0319 if (info->battery_life == -1)
0320 info->battery_life = calculate_capacity(SOURCE_VOLTAGE);
0321 }
0322
0323
0324
0325 if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
0326 info->battery_status = APM_BATTERY_STATUS_CHARGING;
0327 } else {
0328 if (info->battery_life > 50)
0329 info->battery_status = APM_BATTERY_STATUS_HIGH;
0330 else if (info->battery_life > 5)
0331 info->battery_status = APM_BATTERY_STATUS_LOW;
0332 else
0333 info->battery_status = APM_BATTERY_STATUS_CRITICAL;
0334 }
0335 info->battery_flag = info->battery_status;
0336
0337
0338
0339 info->units = APM_UNITS_MINS;
0340
0341 if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
0342 if (!MPSY_PROP(TIME_TO_FULL_AVG, &time_to_full) ||
0343 !MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full))
0344 info->time = time_to_full.intval / 60;
0345 else
0346 info->time = calculate_time(status.intval);
0347 } else {
0348 if (!MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty) ||
0349 !MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty))
0350 info->time = time_to_empty.intval / 60;
0351 else
0352 info->time = calculate_time(status.intval);
0353 }
0354
0355 mutex_unlock(&apm_mutex);
0356 }
0357
0358 static int __init apm_battery_init(void)
0359 {
0360 printk(KERN_INFO "APM Battery Driver\n");
0361
0362 apm_get_power_status = apm_battery_apm_get_power_status;
0363 return 0;
0364 }
0365
0366 static void __exit apm_battery_exit(void)
0367 {
0368 apm_get_power_status = NULL;
0369 }
0370
0371 module_init(apm_battery_init);
0372 module_exit(apm_battery_exit);
0373
0374 MODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>");
0375 MODULE_DESCRIPTION("APM emulation driver for battery monitoring class");
0376 MODULE_LICENSE("GPL");