Linux 电池 驱动

电池驱动 涉及文件

power_supply.h
power_supply_core.c  电池核心
power_supply_leds.c  充电指示灯
power_supply_sysfs.c  数据上报
max17040_battery.c  电源芯片

/*  power_supply_core.c  
注册class
注册uevent
电池信息变化更新事件 power_supply_changed()

power_supply_sysfs.c
事件跟新事件中状态 power_supply_uevent()

max17040_battery.c
注册IIC设备
注册电源属性 max17040_battery_props()
注册延时工作(循环读取电源状态) max17040_work()
事件更新 max17040_get_property()

流程

 max17040_work()-> power_supply_changed()-> power_supply_uevent()  ->  max17040_get_property()



power_supply_core.c

static int __init power_supply_class_init(void)
{
	power_supply_class = class_create(THIS_MODULE, "power_supply");  //注册class到sys

	if (IS_ERR(power_supply_class))
		return PTR_ERR(power_supply_class);

	power_supply_class->dev_uevent = power_supply_uevent;  //注册事件触发函数
	power_supply_init_attrs(&power_supply_dev_type);   //注册属性列表函数

	return 0;
}


subsys_initcall(power_supply_class_init); //初始化


int power_supply_register(struct device *parent, struct power_supply *psy)  //注册电源实例
{
	struct device *dev;
	int rc;

	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
	if (!dev)
		return -ENOMEM;

	device_initialize(dev);

	dev->class = power_supply_class;      //绑定class
	dev->type = &power_supply_dev_type;   //绑定属性列表函数
	dev->parent = parent;
	dev->release = power_supply_dev_release;
	dev_set_drvdata(dev, psy);
	psy->dev = dev;

	rc = kobject_set_name(&dev->kobj, "%s", psy->name);
	if (rc)
		goto kobject_set_name_failed;

	rc = device_add(dev);
	if (rc)
		goto device_add_failed;

	INIT_WORK(&psy->changed_work, power_supply_changed_work);  //注册工作队列
	spin_lock_init(&psy->changed_lock);
	wake_lock_init(&psy->work_wake_lock, WAKE_LOCK_SUSPEND, "power-supply");

	rc = power_supply_create_triggers(psy);
	if (rc)
		goto create_triggers_failed;

	power_supply_changed(psy);  //调度

#if defined CONFIG_VBATTERY_BACKEND || defined CONFIG_VBATTERY_BACKEND_MODULE
	blocking_notifier_call_chain(&vbattery_be_notifier_list, 0, psy);
#endif
	goto success;

create_triggers_failed:
	wake_lock_destroy(&psy->work_wake_lock);
	device_unregister(psy->dev);
kobject_set_name_failed:
device_add_failed:
	kfree(dev);
success:
	return rc;
}


static void power_supply_changed_work(struct work_struct *work)
{
	unsigned long flags;
	struct power_supply *psy = container_of(work, struct power_supply,
						changed_work);

	dev_dbg(psy->dev, "%s\n", __func__);

	spin_lock_irqsave(&psy->changed_lock, flags);
	if (psy->changed) {
		psy->changed = false;
		spin_unlock_irqrestore(&psy->changed_lock, flags);

		class_for_each_device(power_supply_class, NULL, psy,
				      __power_supply_changed_work);

		power_supply_update_leds(psy);

		kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE);//更新事件,触发事件函数
		spin_lock_irqsave(&psy->changed_lock, flags);
	}
	if (!psy->changed)
		wake_unlock(&psy->work_wake_lock);
	spin_unlock_irqrestore(&psy->changed_lock, flags);
}

void power_supply_changed(struct power_supply *psy)
{
	unsigned long flags;

	dev_dbg(psy->dev, "%s\n", __func__);

	spin_lock_irqsave(&psy->changed_lock, flags);
	psy->changed = true;
	wake_lock(&psy->work_wake_lock);
	spin_unlock_irqrestore(&psy->changed_lock, flags);
	schedule_work(&psy->changed_work);   //开始工作
}



--------------------------
power_supply_sysfs.c文件

int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env)//事件函数
{
	struct power_supply *psy = dev_get_drvdata(dev);
	int ret = 0, j;
	char *prop_buf;
	char *attrname;

	dev_dbg(dev, "uevent\n");

	if (!psy || !psy->dev) {
		dev_dbg(dev, "No power supply yet\n");
		return ret;
	}

	dev_dbg(dev, "POWER_SUPPLY_NAME=%s\n", psy->name);

	ret = add_uevent_var(env, "POWER_SUPPLY_NAME=%s", psy->name);
	if (ret)
		return ret;

	prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
	if (!prop_buf)
		return -ENOMEM;

	for (j = 0; j < psy->num_properties; j++) {
		struct device_attribute *attr;
		char *line;

		attr = &power_supply_attrs[psy->properties[j]];

		ret = power_supply_show_property(dev, attr, prop_buf); //更新
		if (ret == -ENODEV) {
			/* When a battery is absent, we expect -ENODEV. Don't abort;
			   send the uevent with at least the the PRESENT=0 property */
			ret = 0;
			continue;
		}

		if (ret < 0)
			goto out;

		line = strchr(prop_buf, '\n');
		if (line)
			*line = 0;

		attrname = kstruprdup(attr->attr.name, GFP_KERNEL);
		if (!attrname) {
			ret = -ENOMEM;
			goto out;
		}

		dev_dbg(dev, "prop %s=%s\n", attrname, prop_buf);

		ret = add_uevent_var(env, "POWER_SUPPLY_%s=%s", attrname, prop_buf);
		kfree(attrname);
		if (ret)
			goto out;
	}

out:
	free_page((unsigned long)prop_buf);

	return ret;
}


static ssize_t power_supply_show_property(struct device *dev,
					  struct device_attribute *attr,
					  char *buf) {
	static char *type_text[] = {
		"Battery", "UPS", "Mains", "USB"
	};
	static char *status_text[] = {
		"Unknown", "Charging", "Discharging", "Not charging", "Full"
	};
	static char *charge_type[] = {
		"Unknown", "N/A", "Trickle", "Fast"
	};
	static char *health_text[] = {
		"Unknown", "Good", "Overheat", "Dead", "Over voltage",
		"Unspecified failure", "Cold",
	};
	static char *technology_text[] = {
		"Unknown", "NiMH", "Li-ion", "Li-poly", "LiFe", "NiCd",
		"LiMn"
	};
	static char *capacity_level_text[] = {
		"Unknown", "Critical", "Low", "Normal", "High", "Full"
	};
	ssize_t ret = 0;
	struct power_supply *psy = dev_get_drvdata(dev);
	const ptrdiff_t off = attr - power_supply_attrs;
	union power_supply_propval value;

	if (off == POWER_SUPPLY_PROP_TYPE)
		value.intval = psy->type;
	else
		ret = psy->get_property(psy, off, &value);//调用电池的get_property更新数据

	if (ret < 0) {
		if (ret == -ENODATA)
			dev_dbg(dev, "driver has no data for `%s' property\n",
				attr->attr.name);
		else if (ret != -ENODEV)
			dev_err(dev, "driver failed to report `%s' property\n",
				attr->attr.name);
		return ret;
	}

	if (off == POWER_SUPPLY_PROP_STATUS)
		return sprintf(buf, "%s\n", status_text[value.intval]);
	else if (off == POWER_SUPPLY_PROP_CHARGE_TYPE)
		return sprintf(buf, "%s\n", charge_type[value.intval]);
	else if (off == POWER_SUPPLY_PROP_HEALTH)
		return sprintf(buf, "%s\n", health_text[value.intval]);
	else if (off == POWER_SUPPLY_PROP_TECHNOLOGY)
		return sprintf(buf, "%s\n", technology_text[value.intval]);
	else if (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL)
		return sprintf(buf, "%s\n", capacity_level_text[value.intval]);
	else if (off == POWER_SUPPLY_PROP_TYPE)
		return sprintf(buf, "%s\n", type_text[value.intval]);
	else if (off >= POWER_SUPPLY_PROP_MODEL_NAME)
		return sprintf(buf, "%s\n", value.strval);

	return sprintf(buf, "%d\n", value.intval);
}

----------------------
max17040_battery.c//文件

static struct i2c_driver max17040_i2c_driver = {
	.driver	= {
		.name	= "max17040",
	},
	.probe		= max17040_probe,
	.remove		= __devexit_p(max17040_remove),
	.suspend	= max17040_suspend,
	.resume		= max17040_resume,
	.id_table	= max17040_id,
};
static int __init max17040_init(void)
{
	return i2c_add_driver(&max17040_i2c_driver);//注册IIC设备
}


static enum power_supply_property max17040_battery_props[] = {
	POWER_SUPPLY_PROP_STATUS,
	POWER_SUPPLY_PROP_ONLINE,
	POWER_SUPPLY_PROP_VOLTAGE_NOW,
	POWER_SUPPLY_PROP_CAPACITY,
};
static int __devinit max17040_probe(struct i2c_client *client,
			const struct i2c_device_id *id)
{
	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
	struct max17040_chip *chip;
	int ret;

	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
		return -EIO;

	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
	if (!chip)
		return -ENOMEM;

	chip->client = client;
	chip->pdata = client->dev.platform_data;

	i2c_set_clientdata(client, chip);

	chip->battery.name		= "battery";//电源名称
	chip->battery.type		= POWER_SUPPLY_TYPE_BATTERY; //属性 :电池
	chip->battery.get_property	= max17040_get_property;  //注册函数get_property
	chip->battery.properties	= max17040_battery_props;   //注册更新电池状态列表
	chip->battery.num_properties	= ARRAY_SIZE(max17040_battery_props);//状态个数

	ret = power_supply_register(&client->dev, &chip->battery);
	if (ret) {
		dev_err(&client->dev, "failed: power supply register\n");
		kfree(chip);
		return ret;
	}

	max17040_reset(client);
	max17040_get_version(client);

	INIT_DELAYED_WORK_DEFERRABLE(&chip->work, max17040_work);//注册延时工作
	schedule_delayed_work(&chip->work, MAX17040_DELAY); //调度延时工作

	return 0;
}


static void max17040_work(struct work_struct *work)  
{  
    struct max17040_chip *chip;  
    int old_usb_online, old_online, old_vcell, old_soc;  
    chip = container_of(work, struct max17040_chip, work.work);  
 
#ifdef MAX17040_SUPPORT_CURVE  
    /* The module need to be update per hour (60*60)/3 = 1200 */  
    if (g_TimeCount >= 1200) {  
        handle_model(0);  
        g_TimeCount = 0;  
    }  
    g_TimeCount++;  
#endif  
  
    old_online = chip->online;//(1)、保存老的电池信息,如电量、AC、USB是否插入;  
    old_usb_online = chip->usb_online;  
    old_vcell = chip->vcell;  
    old_soc = chip->soc;  
    max17040_get_online(chip->client);//(2)、读取电池新的状态信息  
    max17040_get_vcell(chip->client);  
    max17040_get_soc(chip->client);  
    max17040_get_status(chip->client);  
  
    if ((old_vcell != chip->vcell) || (old_soc != chip->soc)) {//(3)、如果电压电量有变化,就上报系统;  
        /* printk(KERN_DEBUG "power_supply_changed for battery\n"); */  
        power_supply_changed(&chip->battery);  
    }  
 
#if !defined(CONFIG_CHARGER_PM2301)//(4)、如果用PM2301充电IC,USB充电功能不用;  
    if (old_usb_online != chip->usb_online) {  
        /* printk(KERN_DEBUG "power_supply_changed for usb\n"); */  
  
        power_supply_changed(&chip->usb);  
    }  
#endif  
  
    if (old_online != chip->online) {//(5)、如果有DC插入,则更新充电状态;  
        /* printk(KERN_DEBUG "power_supply_changed for AC\n"); */  
        power_supply_changed(&chip->ac);  
    }  
  
    schedule_delayed_work(&chip->work, MAX17040_DELAY);  
}  

static void max17040_get_vcell(struct i2c_client *client)
{
	struct max17040_chip *chip = i2c_get_clientdata(client);
	u8 msb;
	u8 lsb;

	msb = max17040_read_reg(client, MAX17040_VCELL_MSB);
	lsb = max17040_read_reg(client, MAX17040_VCELL_LSB);

	chip->vcell = (msb << 4) + (lsb >> 4);
}

static int max17040_get_property(struct power_supply *psy,
			    enum power_supply_property psp,
			    union power_supply_propval *val)
{
	struct max17040_chip *chip = container_of(psy,
				struct max17040_chip, battery);

	switch (psp) {
	case POWER_SUPPLY_PROP_STATUS:
		val->intval = chip->status;
		break;
	case POWER_SUPPLY_PROP_ONLINE:
		val->intval = chip->online;
		break;
	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
		val->intval = chip->vcell;
		break;
	case POWER_SUPPLY_PROP_CAPACITY:
		val->intval = chip->soc;
		break;
	default:
		return -EINVAL;
	}
	return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值