cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

apm_power.c (10386B)


      1/*
      2 * Copyright © 2007 Anton Vorontsov <cbou@mail.ru>
      3 * Copyright © 2007 Eugeny Boger <eugenyboger@dgap.mipt.ru>
      4 *
      5 * Author: Eugeny Boger <eugenyboger@dgap.mipt.ru>
      6 *
      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.
     10 */
     11
     12#include <linux/module.h>
     13#include <linux/device.h>
     14#include <linux/power_supply.h>
     15#include <linux/apm-emulation.h>
     16
     17
     18#define PSY_PROP(psy, prop, val) (power_supply_get_property(psy, \
     19			 POWER_SUPPLY_PROP_##prop, val))
     20
     21#define _MPSY_PROP(prop, val) (power_supply_get_property(main_battery, \
     22							 prop, val))
     23
     24#define MPSY_PROP(prop, val) _MPSY_PROP(POWER_SUPPLY_PROP_##prop, val)
     25
     26static DEFINE_MUTEX(apm_mutex);
     27static struct power_supply *main_battery;
     28
     29enum apm_source {
     30	SOURCE_ENERGY,
     31	SOURCE_CHARGE,
     32	SOURCE_VOLTAGE,
     33};
     34
     35struct find_bat_param {
     36	struct power_supply *main;
     37	struct power_supply *bat;
     38	struct power_supply *max_charge_bat;
     39	struct power_supply *max_energy_bat;
     40	union power_supply_propval full;
     41	int max_charge;
     42	int max_energy;
     43};
     44
     45static int __find_main_battery(struct device *dev, void *data)
     46{
     47	struct find_bat_param *bp = (struct find_bat_param *)data;
     48
     49	bp->bat = dev_get_drvdata(dev);
     50
     51	if (bp->bat->desc->use_for_apm) {
     52		/* nice, we explicitly asked to report this battery. */
     53		bp->main = bp->bat;
     54		return 1;
     55	}
     56
     57	if (!PSY_PROP(bp->bat, CHARGE_FULL_DESIGN, &bp->full) ||
     58			!PSY_PROP(bp->bat, CHARGE_FULL, &bp->full)) {
     59		if (bp->full.intval > bp->max_charge) {
     60			bp->max_charge_bat = bp->bat;
     61			bp->max_charge = bp->full.intval;
     62		}
     63	} else if (!PSY_PROP(bp->bat, ENERGY_FULL_DESIGN, &bp->full) ||
     64			!PSY_PROP(bp->bat, ENERGY_FULL, &bp->full)) {
     65		if (bp->full.intval > bp->max_energy) {
     66			bp->max_energy_bat = bp->bat;
     67			bp->max_energy = bp->full.intval;
     68		}
     69	}
     70	return 0;
     71}
     72
     73static void find_main_battery(void)
     74{
     75	struct find_bat_param bp;
     76	int error;
     77
     78	memset(&bp, 0, sizeof(struct find_bat_param));
     79	main_battery = NULL;
     80	bp.main = main_battery;
     81
     82	error = class_for_each_device(power_supply_class, NULL, &bp,
     83				      __find_main_battery);
     84	if (error) {
     85		main_battery = bp.main;
     86		return;
     87	}
     88
     89	if ((bp.max_energy_bat && bp.max_charge_bat) &&
     90			(bp.max_energy_bat != bp.max_charge_bat)) {
     91		/* try guess battery with more capacity */
     92		if (!PSY_PROP(bp.max_charge_bat, VOLTAGE_MAX_DESIGN,
     93			      &bp.full)) {
     94			if (bp.max_energy > bp.max_charge * bp.full.intval)
     95				main_battery = bp.max_energy_bat;
     96			else
     97				main_battery = bp.max_charge_bat;
     98		} else if (!PSY_PROP(bp.max_energy_bat, VOLTAGE_MAX_DESIGN,
     99								  &bp.full)) {
    100			if (bp.max_charge > bp.max_energy / bp.full.intval)
    101				main_battery = bp.max_charge_bat;
    102			else
    103				main_battery = bp.max_energy_bat;
    104		} else {
    105			/* give up, choice any */
    106			main_battery = bp.max_energy_bat;
    107		}
    108	} else if (bp.max_charge_bat) {
    109		main_battery = bp.max_charge_bat;
    110	} else if (bp.max_energy_bat) {
    111		main_battery = bp.max_energy_bat;
    112	} else {
    113		/* give up, try the last if any */
    114		main_battery = bp.bat;
    115	}
    116}
    117
    118static int do_calculate_time(int status, enum apm_source source)
    119{
    120	union power_supply_propval full;
    121	union power_supply_propval empty;
    122	union power_supply_propval cur;
    123	union power_supply_propval I;
    124	enum power_supply_property full_prop;
    125	enum power_supply_property full_design_prop;
    126	enum power_supply_property empty_prop;
    127	enum power_supply_property empty_design_prop;
    128	enum power_supply_property cur_avg_prop;
    129	enum power_supply_property cur_now_prop;
    130
    131	if (MPSY_PROP(CURRENT_AVG, &I)) {
    132		/* if battery can't report average value, use momentary */
    133		if (MPSY_PROP(CURRENT_NOW, &I))
    134			return -1;
    135	}
    136
    137	if (!I.intval)
    138		return 0;
    139
    140	switch (source) {
    141	case SOURCE_CHARGE:
    142		full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
    143		full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
    144		empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
    145		empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
    146		cur_avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
    147		cur_now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
    148		break;
    149	case SOURCE_ENERGY:
    150		full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
    151		full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
    152		empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
    153		empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
    154		cur_avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
    155		cur_now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
    156		break;
    157	case SOURCE_VOLTAGE:
    158		full_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX;
    159		full_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
    160		empty_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN;
    161		empty_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
    162		cur_avg_prop = POWER_SUPPLY_PROP_VOLTAGE_AVG;
    163		cur_now_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
    164		break;
    165	default:
    166		printk(KERN_ERR "Unsupported source: %d\n", source);
    167		return -1;
    168	}
    169
    170	if (_MPSY_PROP(full_prop, &full)) {
    171		/* if battery can't report this property, use design value */
    172		if (_MPSY_PROP(full_design_prop, &full))
    173			return -1;
    174	}
    175
    176	if (_MPSY_PROP(empty_prop, &empty)) {
    177		/* if battery can't report this property, use design value */
    178		if (_MPSY_PROP(empty_design_prop, &empty))
    179			empty.intval = 0;
    180	}
    181
    182	if (_MPSY_PROP(cur_avg_prop, &cur)) {
    183		/* if battery can't report average value, use momentary */
    184		if (_MPSY_PROP(cur_now_prop, &cur))
    185			return -1;
    186	}
    187
    188	if (status == POWER_SUPPLY_STATUS_CHARGING)
    189		return ((cur.intval - full.intval) * 60L) / I.intval;
    190	else
    191		return -((cur.intval - empty.intval) * 60L) / I.intval;
    192}
    193
    194static int calculate_time(int status)
    195{
    196	int time;
    197
    198	time = do_calculate_time(status, SOURCE_ENERGY);
    199	if (time != -1)
    200		return time;
    201
    202	time = do_calculate_time(status, SOURCE_CHARGE);
    203	if (time != -1)
    204		return time;
    205
    206	time = do_calculate_time(status, SOURCE_VOLTAGE);
    207	if (time != -1)
    208		return time;
    209
    210	return -1;
    211}
    212
    213static int calculate_capacity(enum apm_source source)
    214{
    215	enum power_supply_property full_prop, empty_prop;
    216	enum power_supply_property full_design_prop, empty_design_prop;
    217	enum power_supply_property now_prop, avg_prop;
    218	union power_supply_propval empty, full, cur;
    219	int ret;
    220
    221	switch (source) {
    222	case SOURCE_CHARGE:
    223		full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
    224		empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
    225		full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
    226		empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN;
    227		now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
    228		avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
    229		break;
    230	case SOURCE_ENERGY:
    231		full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
    232		empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
    233		full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
    234		empty_design_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN;
    235		now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
    236		avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
    237		break;
    238	case SOURCE_VOLTAGE:
    239		full_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX;
    240		empty_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN;
    241		full_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
    242		empty_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
    243		now_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
    244		avg_prop = POWER_SUPPLY_PROP_VOLTAGE_AVG;
    245		break;
    246	default:
    247		printk(KERN_ERR "Unsupported source: %d\n", source);
    248		return -1;
    249	}
    250
    251	if (_MPSY_PROP(full_prop, &full)) {
    252		/* if battery can't report this property, use design value */
    253		if (_MPSY_PROP(full_design_prop, &full))
    254			return -1;
    255	}
    256
    257	if (_MPSY_PROP(avg_prop, &cur)) {
    258		/* if battery can't report average value, use momentary */
    259		if (_MPSY_PROP(now_prop, &cur))
    260			return -1;
    261	}
    262
    263	if (_MPSY_PROP(empty_prop, &empty)) {
    264		/* if battery can't report this property, use design value */
    265		if (_MPSY_PROP(empty_design_prop, &empty))
    266			empty.intval = 0;
    267	}
    268
    269	if (full.intval - empty.intval)
    270		ret =  ((cur.intval - empty.intval) * 100L) /
    271		       (full.intval - empty.intval);
    272	else
    273		return -1;
    274
    275	if (ret > 100)
    276		return 100;
    277	else if (ret < 0)
    278		return 0;
    279
    280	return ret;
    281}
    282
    283static void apm_battery_apm_get_power_status(struct apm_power_info *info)
    284{
    285	union power_supply_propval status;
    286	union power_supply_propval capacity, time_to_full, time_to_empty;
    287
    288	mutex_lock(&apm_mutex);
    289	find_main_battery();
    290	if (!main_battery) {
    291		mutex_unlock(&apm_mutex);
    292		return;
    293	}
    294
    295	/* status */
    296
    297	if (MPSY_PROP(STATUS, &status))
    298		status.intval = POWER_SUPPLY_STATUS_UNKNOWN;
    299
    300	/* ac line status */
    301
    302	if ((status.intval == POWER_SUPPLY_STATUS_CHARGING) ||
    303	    (status.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) ||
    304	    (status.intval == POWER_SUPPLY_STATUS_FULL))
    305		info->ac_line_status = APM_AC_ONLINE;
    306	else
    307		info->ac_line_status = APM_AC_OFFLINE;
    308
    309	/* battery life (i.e. capacity, in percents) */
    310
    311	if (MPSY_PROP(CAPACITY, &capacity) == 0) {
    312		info->battery_life = capacity.intval;
    313	} else {
    314		/* try calculate using energy */
    315		info->battery_life = calculate_capacity(SOURCE_ENERGY);
    316		/* if failed try calculate using charge instead */
    317		if (info->battery_life == -1)
    318			info->battery_life = calculate_capacity(SOURCE_CHARGE);
    319		if (info->battery_life == -1)
    320			info->battery_life = calculate_capacity(SOURCE_VOLTAGE);
    321	}
    322
    323	/* charging status */
    324
    325	if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
    326		info->battery_status = APM_BATTERY_STATUS_CHARGING;
    327	} else {
    328		if (info->battery_life > 50)
    329			info->battery_status = APM_BATTERY_STATUS_HIGH;
    330		else if (info->battery_life > 5)
    331			info->battery_status = APM_BATTERY_STATUS_LOW;
    332		else
    333			info->battery_status = APM_BATTERY_STATUS_CRITICAL;
    334	}
    335	info->battery_flag = info->battery_status;
    336
    337	/* time */
    338
    339	info->units = APM_UNITS_MINS;
    340
    341	if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
    342		if (!MPSY_PROP(TIME_TO_FULL_AVG, &time_to_full) ||
    343				!MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full))
    344			info->time = time_to_full.intval / 60;
    345		else
    346			info->time = calculate_time(status.intval);
    347	} else {
    348		if (!MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty) ||
    349			      !MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty))
    350			info->time = time_to_empty.intval / 60;
    351		else
    352			info->time = calculate_time(status.intval);
    353	}
    354
    355	mutex_unlock(&apm_mutex);
    356}
    357
    358static int __init apm_battery_init(void)
    359{
    360	printk(KERN_INFO "APM Battery Driver\n");
    361
    362	apm_get_power_status = apm_battery_apm_get_power_status;
    363	return 0;
    364}
    365
    366static void __exit apm_battery_exit(void)
    367{
    368	apm_get_power_status = NULL;
    369}
    370
    371module_init(apm_battery_init);
    372module_exit(apm_battery_exit);
    373
    374MODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>");
    375MODULE_DESCRIPTION("APM emulation driver for battery monitoring class");
    376MODULE_LICENSE("GPL");