Linux battery 移植的相关概念

1. 一些基本概念

DesignCapacity(DC): 设计电量,就是理论最大电量
FullChargeCapacity(FCC): 充满后的电量,由于环境,温度,重点次数的影响,一般都比DesignCapacity 小。
cycle count: 电池充满电的次数
State Of Health(SOH): 电池的健康度,一般用百分比表示。 电池的健康度一般为电池当前的容量与开始的电池容量之间的比值。一般80%以上则为正常。
State Of Chage (SOC): 即充电状态,是用来反映电池的剩余容量的,其数值上定义为剩余容量占电池容量的比值
RelativeStateOfChage (RSOC): 相对充电状态
AbsoluteStateOfChage(ASOC): 绝对充电状态

RSOC = Remaining Capacity / FCC
ASOC = Remaining Capacity / Design Capacity

相对充电状态总是从0% 到 100%, FCC 会随着充电次数,环境等因素逐渐递减。
绝对充电状态可能,永远达不到100%,因为Design Capacity 是一个固定值,因为FCC 总会随着时间等因素缩减。

2. Linux Battery 驱动简介

电池驱动一般属于linux 的power_supply 子系统,电池驱动需要两个power supply driver, 一个描述电池的基本信息,如DC,FCC,SOH,SOC 等,另外一个描述电池充电器的插入/拔出状态,因为有些系统,通过这个来判断电池是否处于充电状态。

static int hy4231_probe(struct i2c_client *client,
			    const struct i2c_device_id *id)
{
	.......
	/* 该power supply driver 用于描述电池
	 的基本信息 */
	memset(&psy_cfg, 0, sizeof(psy_cfg));
	psy_cfg.drv_data = battery;
	battery->bat = power_supply_register(battery->dev, &hy4231_bat_desc, &psy_cfg);
	if (IS_ERR(battery->bat)) {
		dev_err(battery->dev, "register bat power supply fail\n");
		return PTR_ERR(battery->bat);
	}

	/* 该power supply driver 用于描述电池充电器的
	连接状态 */
	battery->ac = power_supply_register(battery->dev, &hy4231_adapter_desc, &psy_cfg);
	if (IS_ERR(battery->ac)) {
		dev_err(battery->dev, "register ac power supply fail\n");
		return PTR_ERR(battery->ac);
	}

	/* 创建一个延时队列,周期性地获取电池信息,如果电池
	有中断,就不需要了*/
	battery->bat_monitor_wq =
		alloc_ordered_workqueue("%s",
					WQ_MEM_RECLAIM | WQ_FREEZABLE,
					"hy4231-monitor-wq");
	INIT_DELAYED_WORK(&battery->bat_delay_work, hy4231_battery_work);
	queue_delayed_work(battery->bat_monitor_wq, &battery->bat_delay_work,
			   msecs_to_jiffies(TIMER_MS_COUNTS * 10));

	return ret;
}

以上的代码中,最关键的部分就是power_supply_register 函数,该函数注册了一堆sysfs 下的property属性。 具体定义在hy4231_bat_desc 和 hy4231_adapter_desc 结构体中。我们先来看描述电池基本信息的hy4231_bat_desc 结构。

2.1 电池基本信息驱动

hy4231_bat_desc 结构体实质是定义了以对电池基本属性信息,例如FCC, DC,温度,SOC等。

static enum power_supply_property hy4231_bat_props[] = {
	POWER_SUPPLY_PROP_CURRENT_NOW,  /* 电池当前电流 uA*/
	POWER_SUPPLY_PROP_VOLTAGE_NOW,  /* 电池当前电压 uV*/
	POWER_SUPPLY_PROP_PRESENT,   /* 电池当前是否存在 1 存在,0: 不存在*/
	POWER_SUPPLY_PROP_HEALTH,      /* 电池的健康度 0-100 */
	POWER_SUPPLY_PROP_CAPACITY,    /* 电池的当前电量,也就是SOC 0-100 */
	POWER_SUPPLY_PROP_TEMP,     /* 电池的温度 =  K- 2731, 最终是摄氏温度值*10 */
	POWER_SUPPLY_PROP_STATUS,  /* 电池的充电状态 和 后面讲的POWER_SUPPLY_PROP_ONLINE 搭配使用 */
	POWER_SUPPLY_PROP_TECHNOLOGY, /* 电池所使用的充电技术,像Li+等 */
	POWER_SUPPLY_PROP_CHARGE_FULL,  /* 电池FCC */
	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,  /* 电池设计容量 */
	POWER_SUPPLY_PROP_CYCLE_COUNT,  /* 电池的充电次数*/
};

static const struct power_supply_desc hy4231_bat_desc = {
	.name		= "hy4231-bat",
	.type		= POWER_SUPPLY_TYPE_BATTERY,
	.properties	= hy4231_bat_props,
	.num_properties	= ARRAY_SIZE(hy4231_bat_props),
	.get_property	= hy4231_bat_get_property,
};

POWER_SUPPLY_TYPE_BATTERY 表示该power supply 为一个电池基本信息的driver。
这些宏定义,最后都会生成具体的sysfs 属性节点,它们位于/sys/class/power_supply/${driver_name}/ 下, 用户只需要读取这些sysfs 节点的值就可以了。
如何获取这些信息值呢,就是上面结构体实现的get_property 方法,我们来看下大概:

static int hy4231_bat_get_property(struct power_supply *psy,
				   enum power_supply_property psp,
				   union power_supply_propval *val)
{
	struct hy4231_battery *battery = power_supply_get_drvdata(psy);

	switch (psp) {
	case POWER_SUPPLY_PROP_CURRENT_NOW:
		val->intval = battery->current_now;	/*uA*/
		val->intval *= 1000;
		break;
	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
		val->intval = battery->voltage_now;/*uV*/
		val->intval *= 1000;
		break;
	case POWER_SUPPLY_PROP_PRESENT:
		val->intval = is_hy4231_bat_exist(battery);
		break;
	case POWER_SUPPLY_PROP_CAPACITY:
		val->intval = battery->soc;
		break;
	case POWER_SUPPLY_PROP_HEALTH:
		if(battery->health_state >= 80)
		     /* 电池是健康的*/
			val->intval = POWER_SUPPLY_HEALTH_GOOD;
		else
			val->intval = POWER_SUPPLY_HEALTH_DEAD;	
		break;
	case POWER_SUPPLY_PROP_TEMP:
		val->intval = battery->temperature_now;
		break;
	case POWER_SUPPLY_PROP_CYCLE_COUNT:
		val->intval = battery->cycle_count;
		break;
	case POWER_SUPPLY_PROP_CHARGE_FULL:
		val->intval = battery->full_charge_capacity;
		val->intval *= 1000;
		break;
	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
		val->intval = battery->design_capacity;
		val->intval *= 1000;
		break;
	case POWER_SUPPLY_PROP_TECHNOLOGY:
		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
		break;
	case POWER_SUPPLY_PROP_STATUS:
		if (battery->soc >= 100)
			val->intval = POWER_SUPPLY_STATUS_FULL;
		else if (hy4231_bat_chrg_online(battery))
			val->intval = POWER_SUPPLY_STATUS_CHARGING;
		else
			val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
		break;
	default:
		return -EINVAL;
	}

	return 0;
}

如果你希望可以更改这些值,也可以实现对应的set_property 的方法。

2.2 电池充电器状态驱动

hy4231_adapter_desc 描述了电池充电器插拔状态的信息,详细定义如下:

static int hy4231_ac_get_property(struct power_supply *psy,
				   enum power_supply_property psp,
				   union power_supply_propval *val)
{
	struct hy4231_battery *battery = power_supply_get_drvdata(psy);

	switch (psp) {
	case POWER_SUPPLY_PROP_ONLINE:
		val->intval = hy4231_bat_chrg_online(battery);
		break;
	default:
		return -EINVAL;
	}

	return 0;
}

static enum power_supply_property hy4231_ac_props[] = {
	POWER_SUPPLY_PROP_ONLINE,  /*充电线是否在线,插入/拔出? */
};

static const struct power_supply_desc hy4231_adapter_desc = {
	.name		= "hy4231-ac",
	.type		= POWER_SUPPLY_TYPE_MAINS,
	.properties	= hy4231_ac_props,
	.num_properties	= ARRAY_SIZE(hy4231_ac_props),
	.get_property	= hy4231_ac_get_property,
};

POWER_SUPPLY_TYPE_MAINS 表示该power supply 是一个主供电设备,类似于笔记本电脑,当adapter 存在时,它提供电源,当adapter 拔出时,电池battery 为系统提供电源。
该driver 中只有一个sysfs 的property, POWER_SUPPLY_PROP_ONLINE, 表示充电器有没有插入/拔出。很多OS 像Android,就通过POWER_SUPPLY_PROP_STATUS 和POWER_SUPPLY_PROP_ONLINE 共同来判断电池是否处于充电状态。

有了以上信息,就可以读取所有电池信息,然而驱动并不能主动向用户层通知,开始充电,充电结束,电量变化等状态,这就用上了,我们前面讲的probe 中,创建的延迟队列了。

2.3 电池的工作队列

电池的工作队列中有一个worker, 周期性的获取电池信息,如果产生改变,将通过uevent 的方式,告诉用户,相关改变,用户再去读对应的sysfs,这样大大节省了用户的时间,提升了处理效率。

static void hy4231_battery_work(struct work_struct *work)
{
	struct hy4231_battery *battery =
		container_of(work, struct hy4231_battery, bat_delay_work.work);

    /*
	获取电池信息, 充电器连接信息,
	如果发生状态改变, 通过power_supply_changed(xxx)
	告知用户层,电池信息发生改变;
    */ 
    get_all_battery_info(xxx)
	power_supply_changed(xxx)

	//假如等待队列,周期性执行该worker。
	queue_delayed_work(battery->bat_monitor_wq, &battery->bat_delay_work,
			   msecs_to_jiffies(battery->monitor_sec));
}

power_supply_changed 函数会以uevent 的方式告知用户层,用户接受到对应信息后,再读取sysfs 相关属性信息。

至此,我们已经大致描述清楚了一个简单的可充电电池的driver 需要做的部分,希望对你有用!

你可能感兴趣的:(linux,battery)