18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright © 2007 Anton Vorontsov <cbou@mail.ru>
38c2ecf20Sopenharmony_ci * Copyright © 2007 Eugeny Boger <eugenyboger@dgap.mipt.ru>
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Author: Eugeny Boger <eugenyboger@dgap.mipt.ru>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Use consistent with the GNU GPL is permitted,
88c2ecf20Sopenharmony_ci * provided that this copyright notice is
98c2ecf20Sopenharmony_ci * preserved in its entirety in all copies and derived works.
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/device.h>
148c2ecf20Sopenharmony_ci#include <linux/power_supply.h>
158c2ecf20Sopenharmony_ci#include <linux/apm-emulation.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define PSY_PROP(psy, prop, val) (power_supply_get_property(psy, \
198c2ecf20Sopenharmony_ci			 POWER_SUPPLY_PROP_##prop, val))
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define _MPSY_PROP(prop, val) (power_supply_get_property(main_battery, \
228c2ecf20Sopenharmony_ci							 prop, val))
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define MPSY_PROP(prop, val) _MPSY_PROP(POWER_SUPPLY_PROP_##prop, val)
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(apm_mutex);
278c2ecf20Sopenharmony_cistatic struct power_supply *main_battery;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cienum apm_source {
308c2ecf20Sopenharmony_ci	SOURCE_ENERGY,
318c2ecf20Sopenharmony_ci	SOURCE_CHARGE,
328c2ecf20Sopenharmony_ci	SOURCE_VOLTAGE,
338c2ecf20Sopenharmony_ci};
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistruct find_bat_param {
368c2ecf20Sopenharmony_ci	struct power_supply *main;
378c2ecf20Sopenharmony_ci	struct power_supply *bat;
388c2ecf20Sopenharmony_ci	struct power_supply *max_charge_bat;
398c2ecf20Sopenharmony_ci	struct power_supply *max_energy_bat;
408c2ecf20Sopenharmony_ci	union power_supply_propval full;
418c2ecf20Sopenharmony_ci	int max_charge;
428c2ecf20Sopenharmony_ci	int max_energy;
438c2ecf20Sopenharmony_ci};
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic int __find_main_battery(struct device *dev, void *data)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	struct find_bat_param *bp = (struct find_bat_param *)data;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	bp->bat = dev_get_drvdata(dev);
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	if (bp->bat->desc->use_for_apm) {
528c2ecf20Sopenharmony_ci		/* nice, we explicitly asked to report this battery. */
538c2ecf20Sopenharmony_ci		bp->main = bp->bat;
548c2ecf20Sopenharmony_ci		return 1;
558c2ecf20Sopenharmony_ci	}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	if (!PSY_PROP(bp->bat, CHARGE_FULL_DESIGN, &bp->full) ||
588c2ecf20Sopenharmony_ci			!PSY_PROP(bp->bat, CHARGE_FULL, &bp->full)) {
598c2ecf20Sopenharmony_ci		if (bp->full.intval > bp->max_charge) {
608c2ecf20Sopenharmony_ci			bp->max_charge_bat = bp->bat;
618c2ecf20Sopenharmony_ci			bp->max_charge = bp->full.intval;
628c2ecf20Sopenharmony_ci		}
638c2ecf20Sopenharmony_ci	} else if (!PSY_PROP(bp->bat, ENERGY_FULL_DESIGN, &bp->full) ||
648c2ecf20Sopenharmony_ci			!PSY_PROP(bp->bat, ENERGY_FULL, &bp->full)) {
658c2ecf20Sopenharmony_ci		if (bp->full.intval > bp->max_energy) {
668c2ecf20Sopenharmony_ci			bp->max_energy_bat = bp->bat;
678c2ecf20Sopenharmony_ci			bp->max_energy = bp->full.intval;
688c2ecf20Sopenharmony_ci		}
698c2ecf20Sopenharmony_ci	}
708c2ecf20Sopenharmony_ci	return 0;
718c2ecf20Sopenharmony_ci}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic void find_main_battery(void)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	struct find_bat_param bp;
768c2ecf20Sopenharmony_ci	int error;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	memset(&bp, 0, sizeof(struct find_bat_param));
798c2ecf20Sopenharmony_ci	main_battery = NULL;
808c2ecf20Sopenharmony_ci	bp.main = main_battery;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	error = class_for_each_device(power_supply_class, NULL, &bp,
838c2ecf20Sopenharmony_ci				      __find_main_battery);
848c2ecf20Sopenharmony_ci	if (error) {
858c2ecf20Sopenharmony_ci		main_battery = bp.main;
868c2ecf20Sopenharmony_ci		return;
878c2ecf20Sopenharmony_ci	}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	if ((bp.max_energy_bat && bp.max_charge_bat) &&
908c2ecf20Sopenharmony_ci			(bp.max_energy_bat != bp.max_charge_bat)) {
918c2ecf20Sopenharmony_ci		/* try guess battery with more capacity */
928c2ecf20Sopenharmony_ci		if (!PSY_PROP(bp.max_charge_bat, VOLTAGE_MAX_DESIGN,
938c2ecf20Sopenharmony_ci			      &bp.full)) {
948c2ecf20Sopenharmony_ci			if (bp.max_energy > bp.max_charge * bp.full.intval)
958c2ecf20Sopenharmony_ci				main_battery = bp.max_energy_bat;
968c2ecf20Sopenharmony_ci			else
978c2ecf20Sopenharmony_ci				main_battery = bp.max_charge_bat;
988c2ecf20Sopenharmony_ci		} else if (!PSY_PROP(bp.max_energy_bat, VOLTAGE_MAX_DESIGN,
998c2ecf20Sopenharmony_ci								  &bp.full)) {
1008c2ecf20Sopenharmony_ci			if (bp.max_charge > bp.max_energy / bp.full.intval)
1018c2ecf20Sopenharmony_ci				main_battery = bp.max_charge_bat;
1028c2ecf20Sopenharmony_ci			else
1038c2ecf20Sopenharmony_ci				main_battery = bp.max_energy_bat;
1048c2ecf20Sopenharmony_ci		} else {
1058c2ecf20Sopenharmony_ci			/* give up, choice any */
1068c2ecf20Sopenharmony_ci			main_battery = bp.max_energy_bat;
1078c2ecf20Sopenharmony_ci		}
1088c2ecf20Sopenharmony_ci	} else if (bp.max_charge_bat) {
1098c2ecf20Sopenharmony_ci		main_battery = bp.max_charge_bat;
1108c2ecf20Sopenharmony_ci	} else if (bp.max_energy_bat) {
1118c2ecf20Sopenharmony_ci		main_battery = bp.max_energy_bat;
1128c2ecf20Sopenharmony_ci	} else {
1138c2ecf20Sopenharmony_ci		/* give up, try the last if any */
1148c2ecf20Sopenharmony_ci		main_battery = bp.bat;
1158c2ecf20Sopenharmony_ci	}
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic int do_calculate_time(int status, enum apm_source source)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	union power_supply_propval full;
1218c2ecf20Sopenharmony_ci	union power_supply_propval empty;
1228c2ecf20Sopenharmony_ci	union power_supply_propval cur;
1238c2ecf20Sopenharmony_ci	union power_supply_propval I;
1248c2ecf20Sopenharmony_ci	enum power_supply_property full_prop;
1258c2ecf20Sopenharmony_ci	enum power_supply_property full_design_prop;
1268c2ecf20Sopenharmony_ci	enum power_supply_property empty_prop;
1278c2ecf20Sopenharmony_ci	enum power_supply_property empty_design_prop;
1288c2ecf20Sopenharmony_ci	enum power_supply_property cur_avg_prop;
1298c2ecf20Sopenharmony_ci	enum power_supply_property cur_now_prop;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	if (MPSY_PROP(CURRENT_AVG, &I)) {
1328c2ecf20Sopenharmony_ci		/* if battery can't report average value, use momentary */
1338c2ecf20Sopenharmony_ci		if (MPSY_PROP(CURRENT_NOW, &I))
1348c2ecf20Sopenharmony_ci			return -1;
1358c2ecf20Sopenharmony_ci	}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	if (!I.intval)
1388c2ecf20Sopenharmony_ci		return 0;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	switch (source) {
1418c2ecf20Sopenharmony_ci	case SOURCE_CHARGE:
1428c2ecf20Sopenharmony_ci		full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
1438c2ecf20Sopenharmony_ci		full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
1448c2ecf20Sopenharmony_ci		empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
1458c2ecf20Sopenharmony_ci		empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
1468c2ecf20Sopenharmony_ci		cur_avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
1478c2ecf20Sopenharmony_ci		cur_now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
1488c2ecf20Sopenharmony_ci		break;
1498c2ecf20Sopenharmony_ci	case SOURCE_ENERGY:
1508c2ecf20Sopenharmony_ci		full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
1518c2ecf20Sopenharmony_ci		full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
1528c2ecf20Sopenharmony_ci		empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
1538c2ecf20Sopenharmony_ci		empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
1548c2ecf20Sopenharmony_ci		cur_avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
1558c2ecf20Sopenharmony_ci		cur_now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
1568c2ecf20Sopenharmony_ci		break;
1578c2ecf20Sopenharmony_ci	case SOURCE_VOLTAGE:
1588c2ecf20Sopenharmony_ci		full_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX;
1598c2ecf20Sopenharmony_ci		full_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
1608c2ecf20Sopenharmony_ci		empty_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN;
1618c2ecf20Sopenharmony_ci		empty_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
1628c2ecf20Sopenharmony_ci		cur_avg_prop = POWER_SUPPLY_PROP_VOLTAGE_AVG;
1638c2ecf20Sopenharmony_ci		cur_now_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
1648c2ecf20Sopenharmony_ci		break;
1658c2ecf20Sopenharmony_ci	default:
1668c2ecf20Sopenharmony_ci		printk(KERN_ERR "Unsupported source: %d\n", source);
1678c2ecf20Sopenharmony_ci		return -1;
1688c2ecf20Sopenharmony_ci	}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	if (_MPSY_PROP(full_prop, &full)) {
1718c2ecf20Sopenharmony_ci		/* if battery can't report this property, use design value */
1728c2ecf20Sopenharmony_ci		if (_MPSY_PROP(full_design_prop, &full))
1738c2ecf20Sopenharmony_ci			return -1;
1748c2ecf20Sopenharmony_ci	}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	if (_MPSY_PROP(empty_prop, &empty)) {
1778c2ecf20Sopenharmony_ci		/* if battery can't report this property, use design value */
1788c2ecf20Sopenharmony_ci		if (_MPSY_PROP(empty_design_prop, &empty))
1798c2ecf20Sopenharmony_ci			empty.intval = 0;
1808c2ecf20Sopenharmony_ci	}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	if (_MPSY_PROP(cur_avg_prop, &cur)) {
1838c2ecf20Sopenharmony_ci		/* if battery can't report average value, use momentary */
1848c2ecf20Sopenharmony_ci		if (_MPSY_PROP(cur_now_prop, &cur))
1858c2ecf20Sopenharmony_ci			return -1;
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	if (status == POWER_SUPPLY_STATUS_CHARGING)
1898c2ecf20Sopenharmony_ci		return ((cur.intval - full.intval) * 60L) / I.intval;
1908c2ecf20Sopenharmony_ci	else
1918c2ecf20Sopenharmony_ci		return -((cur.intval - empty.intval) * 60L) / I.intval;
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic int calculate_time(int status)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	int time;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	time = do_calculate_time(status, SOURCE_ENERGY);
1998c2ecf20Sopenharmony_ci	if (time != -1)
2008c2ecf20Sopenharmony_ci		return time;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	time = do_calculate_time(status, SOURCE_CHARGE);
2038c2ecf20Sopenharmony_ci	if (time != -1)
2048c2ecf20Sopenharmony_ci		return time;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	time = do_calculate_time(status, SOURCE_VOLTAGE);
2078c2ecf20Sopenharmony_ci	if (time != -1)
2088c2ecf20Sopenharmony_ci		return time;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	return -1;
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cistatic int calculate_capacity(enum apm_source source)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	enum power_supply_property full_prop, empty_prop;
2168c2ecf20Sopenharmony_ci	enum power_supply_property full_design_prop, empty_design_prop;
2178c2ecf20Sopenharmony_ci	enum power_supply_property now_prop, avg_prop;
2188c2ecf20Sopenharmony_ci	union power_supply_propval empty, full, cur;
2198c2ecf20Sopenharmony_ci	int ret;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	switch (source) {
2228c2ecf20Sopenharmony_ci	case SOURCE_CHARGE:
2238c2ecf20Sopenharmony_ci		full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
2248c2ecf20Sopenharmony_ci		empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
2258c2ecf20Sopenharmony_ci		full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
2268c2ecf20Sopenharmony_ci		empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN;
2278c2ecf20Sopenharmony_ci		now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
2288c2ecf20Sopenharmony_ci		avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
2298c2ecf20Sopenharmony_ci		break;
2308c2ecf20Sopenharmony_ci	case SOURCE_ENERGY:
2318c2ecf20Sopenharmony_ci		full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
2328c2ecf20Sopenharmony_ci		empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
2338c2ecf20Sopenharmony_ci		full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
2348c2ecf20Sopenharmony_ci		empty_design_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN;
2358c2ecf20Sopenharmony_ci		now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
2368c2ecf20Sopenharmony_ci		avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
2378c2ecf20Sopenharmony_ci		break;
2388c2ecf20Sopenharmony_ci	case SOURCE_VOLTAGE:
2398c2ecf20Sopenharmony_ci		full_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX;
2408c2ecf20Sopenharmony_ci		empty_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN;
2418c2ecf20Sopenharmony_ci		full_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
2428c2ecf20Sopenharmony_ci		empty_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
2438c2ecf20Sopenharmony_ci		now_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
2448c2ecf20Sopenharmony_ci		avg_prop = POWER_SUPPLY_PROP_VOLTAGE_AVG;
2458c2ecf20Sopenharmony_ci		break;
2468c2ecf20Sopenharmony_ci	default:
2478c2ecf20Sopenharmony_ci		printk(KERN_ERR "Unsupported source: %d\n", source);
2488c2ecf20Sopenharmony_ci		return -1;
2498c2ecf20Sopenharmony_ci	}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	if (_MPSY_PROP(full_prop, &full)) {
2528c2ecf20Sopenharmony_ci		/* if battery can't report this property, use design value */
2538c2ecf20Sopenharmony_ci		if (_MPSY_PROP(full_design_prop, &full))
2548c2ecf20Sopenharmony_ci			return -1;
2558c2ecf20Sopenharmony_ci	}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	if (_MPSY_PROP(avg_prop, &cur)) {
2588c2ecf20Sopenharmony_ci		/* if battery can't report average value, use momentary */
2598c2ecf20Sopenharmony_ci		if (_MPSY_PROP(now_prop, &cur))
2608c2ecf20Sopenharmony_ci			return -1;
2618c2ecf20Sopenharmony_ci	}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	if (_MPSY_PROP(empty_prop, &empty)) {
2648c2ecf20Sopenharmony_ci		/* if battery can't report this property, use design value */
2658c2ecf20Sopenharmony_ci		if (_MPSY_PROP(empty_design_prop, &empty))
2668c2ecf20Sopenharmony_ci			empty.intval = 0;
2678c2ecf20Sopenharmony_ci	}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	if (full.intval - empty.intval)
2708c2ecf20Sopenharmony_ci		ret =  ((cur.intval - empty.intval) * 100L) /
2718c2ecf20Sopenharmony_ci		       (full.intval - empty.intval);
2728c2ecf20Sopenharmony_ci	else
2738c2ecf20Sopenharmony_ci		return -1;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	if (ret > 100)
2768c2ecf20Sopenharmony_ci		return 100;
2778c2ecf20Sopenharmony_ci	else if (ret < 0)
2788c2ecf20Sopenharmony_ci		return 0;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	return ret;
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cistatic void apm_battery_apm_get_power_status(struct apm_power_info *info)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	union power_supply_propval status;
2868c2ecf20Sopenharmony_ci	union power_supply_propval capacity, time_to_full, time_to_empty;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	mutex_lock(&apm_mutex);
2898c2ecf20Sopenharmony_ci	find_main_battery();
2908c2ecf20Sopenharmony_ci	if (!main_battery) {
2918c2ecf20Sopenharmony_ci		mutex_unlock(&apm_mutex);
2928c2ecf20Sopenharmony_ci		return;
2938c2ecf20Sopenharmony_ci	}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	/* status */
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	if (MPSY_PROP(STATUS, &status))
2988c2ecf20Sopenharmony_ci		status.intval = POWER_SUPPLY_STATUS_UNKNOWN;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	/* ac line status */
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	if ((status.intval == POWER_SUPPLY_STATUS_CHARGING) ||
3038c2ecf20Sopenharmony_ci	    (status.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) ||
3048c2ecf20Sopenharmony_ci	    (status.intval == POWER_SUPPLY_STATUS_FULL))
3058c2ecf20Sopenharmony_ci		info->ac_line_status = APM_AC_ONLINE;
3068c2ecf20Sopenharmony_ci	else
3078c2ecf20Sopenharmony_ci		info->ac_line_status = APM_AC_OFFLINE;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	/* battery life (i.e. capacity, in percents) */
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	if (MPSY_PROP(CAPACITY, &capacity) == 0) {
3128c2ecf20Sopenharmony_ci		info->battery_life = capacity.intval;
3138c2ecf20Sopenharmony_ci	} else {
3148c2ecf20Sopenharmony_ci		/* try calculate using energy */
3158c2ecf20Sopenharmony_ci		info->battery_life = calculate_capacity(SOURCE_ENERGY);
3168c2ecf20Sopenharmony_ci		/* if failed try calculate using charge instead */
3178c2ecf20Sopenharmony_ci		if (info->battery_life == -1)
3188c2ecf20Sopenharmony_ci			info->battery_life = calculate_capacity(SOURCE_CHARGE);
3198c2ecf20Sopenharmony_ci		if (info->battery_life == -1)
3208c2ecf20Sopenharmony_ci			info->battery_life = calculate_capacity(SOURCE_VOLTAGE);
3218c2ecf20Sopenharmony_ci	}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	/* charging status */
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
3268c2ecf20Sopenharmony_ci		info->battery_status = APM_BATTERY_STATUS_CHARGING;
3278c2ecf20Sopenharmony_ci	} else {
3288c2ecf20Sopenharmony_ci		if (info->battery_life > 50)
3298c2ecf20Sopenharmony_ci			info->battery_status = APM_BATTERY_STATUS_HIGH;
3308c2ecf20Sopenharmony_ci		else if (info->battery_life > 5)
3318c2ecf20Sopenharmony_ci			info->battery_status = APM_BATTERY_STATUS_LOW;
3328c2ecf20Sopenharmony_ci		else
3338c2ecf20Sopenharmony_ci			info->battery_status = APM_BATTERY_STATUS_CRITICAL;
3348c2ecf20Sopenharmony_ci	}
3358c2ecf20Sopenharmony_ci	info->battery_flag = info->battery_status;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	/* time */
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	info->units = APM_UNITS_MINS;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
3428c2ecf20Sopenharmony_ci		if (!MPSY_PROP(TIME_TO_FULL_AVG, &time_to_full) ||
3438c2ecf20Sopenharmony_ci				!MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full))
3448c2ecf20Sopenharmony_ci			info->time = time_to_full.intval / 60;
3458c2ecf20Sopenharmony_ci		else
3468c2ecf20Sopenharmony_ci			info->time = calculate_time(status.intval);
3478c2ecf20Sopenharmony_ci	} else {
3488c2ecf20Sopenharmony_ci		if (!MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty) ||
3498c2ecf20Sopenharmony_ci			      !MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty))
3508c2ecf20Sopenharmony_ci			info->time = time_to_empty.intval / 60;
3518c2ecf20Sopenharmony_ci		else
3528c2ecf20Sopenharmony_ci			info->time = calculate_time(status.intval);
3538c2ecf20Sopenharmony_ci	}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	mutex_unlock(&apm_mutex);
3568c2ecf20Sopenharmony_ci}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_cistatic int __init apm_battery_init(void)
3598c2ecf20Sopenharmony_ci{
3608c2ecf20Sopenharmony_ci	printk(KERN_INFO "APM Battery Driver\n");
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	apm_get_power_status = apm_battery_apm_get_power_status;
3638c2ecf20Sopenharmony_ci	return 0;
3648c2ecf20Sopenharmony_ci}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_cistatic void __exit apm_battery_exit(void)
3678c2ecf20Sopenharmony_ci{
3688c2ecf20Sopenharmony_ci	apm_get_power_status = NULL;
3698c2ecf20Sopenharmony_ci}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_cimodule_init(apm_battery_init);
3728c2ecf20Sopenharmony_cimodule_exit(apm_battery_exit);
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ciMODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>");
3758c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("APM emulation driver for battery monitoring class");
3768c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
377