Linux power_supply子系统

Linux power_supply子系统

1.概念

power supply子系统的引入是由于设备中通常存在多个psy设备,不同的psy设备作用不同,比如电池、DC充电和usb充电等。驱动使用power suply框架

把不同的psy设备都注册成单一节点,当属性变化时,通过uevent的方式通知应用

2.Power Supply Framework 架构

内核抽象power supply子系统为驱动提供了统一的架构,其中功能包括:抽象PSY设备的共性,向用户空间提供统一的API,为底层PSY驱动的编写,提供简单、统一的方式。下图展现power supply子系统的架构图

请添加图片描述

1.power supply core,用于抽象核心数据结构、实现公共逻辑。位于drivers/power/power_supply_core.c中。

2.power supply sysfs,实现sysfs以及uevent功能。位于drivers/power/power_supply_sysfs.c中。

3.power supply leds,基于linux led class,提供PSY设备状态指示的通用实现。位于drivers/power/power_suppply_leds.c中。

最后,驱动工程师可以基于power supply class,实现具体的PSY drivers,主要处理平台相关、硬件相关的逻辑。这些drivers都位于drivers/power/目录下。

3.Power Supply Api

struct power_supply抽象psy设备,用来描述所有的psy设备,后续创建psy设备,只需要在数据结构中申明struct power_supply

1.power supply的结构体

1.power_supply

psy设备

struct power_supply {
	const struct power_supply_desc *desc;		//psy的属性
	char **supplied_to;							//一个字符串数组,保存了由该PSY供电的PSY列表,以此可将PSY组成互相级联的PSY链表。这些被供电的PSY,称作supplicant(客户端,乞求者)
	size_t num_supplicants;						//supplicant的个数

	char **supplied_from;	//一个字符串数组,保存了该PSY供电的PSY列表,也称作supply(提供者),从另外一个方向组织PSY之间的级联关系。
	size_t num_supplies;	//supply的个数
	struct device_node *of_node;

	/* Driver private data */
	void *drv_data;

	/* private */
	struct device dev;
	struct work_struct changed_work;	//用户处理状态改变的work queue,主要思路是当该PSY的状态发生改变,启动一个workqueue,查询并通知所有的supplicants。
	struct delayed_work deferred_register_work;
	spinlock_t changed_lock;
	bool changed;
	bool initialized;
	bool removing;
	atomic_t use_cnt;
#ifdef CONFIG_THERMAL
	struct thermal_zone_device *tzd;
	struct thermal_cooling_device *tcd;
#endif

#ifdef CONFIG_LEDS_TRIGGERS
	struct led_trigger *charging_full_trig;
	char *charging_full_trig_name;
	struct led_trigger *charging_trig;
	char *charging_trig_name;
	struct led_trigger *full_trig;
	char *full_trig_name;
	struct led_trigger *online_trig;
	char *online_trig_name;
	struct led_trigger *charging_blink_full_solid_trig;
	char *charging_blink_full_solid_trig_name;
#endif
};
2.power_supply_desc

psy的属性参数

/* Description of power supply */
struct power_supply_desc {
	const char *name;			//psy name
	enum power_supply_type type;	//psy type ex:dc usb battery
	enum power_supply_usb_type *usb_types;
	size_t num_usb_types;
	enum power_supply_property *properties;	//psy 属性
	size_t num_properties;			//psy属性个数

	/*
	 * Functions for drivers implementing power supply class.
	 * These shouldn't be called directly by other drivers for accessing
	 * this power supply. Instead use power_supply_*() functions (for
	 * example power_supply_get_property()).
	 */
	int (*get_property)(struct power_supply *psy,		//psy的回调函数用于获取psy属性的参数
			    enum power_supply_property psp,
			    union power_supply_propval *val);
	int (*set_property)(struct power_supply *psy,		//psy的回调函数用于设置psy属性的参数
			    enum power_supply_property psp,
			    const union power_supply_propval *val);
	/*
	 * property_is_writeable() will be called during registration
	 * of power supply. If this happens during device probe then it must
	 * not access internal data of device (because probe did not end).
	 */
	int (*property_is_writeable)(struct power_supply *psy,		//返回指定的属性值是否可写(用于sysfs);
				     enum power_supply_property psp);
	void (*external_power_changed)(struct power_supply *psy);	//当一个PSY设备存在supply PSY,且该supply PSY的属性发生改变(如online offline)时,power supply core会调用该回调函数,通知PSY driver以便让它做出相应处理。
	void (*set_charged)(struct power_supply *psy);				/该回调函数的应用场景有点奇怪,外部模块通知PAY driver,该PSY设备的状态改变了,自己改变了自己不知道,要外部通知,希望大家在实际工作中不要遇到,太纠结了。

	/*
	 * Set if thermal zone should not be created for this power supply.
	 * For example for virtual supplies forwarding calls to actual
	 * sensors or other supplies.
	 */
	bool no_thermal;
	/* For APM emulation, think legacy userspace. */
	int use_for_apm;
};
3.power_supply_type

psy设备的类型

enum power_supply_type {
	POWER_SUPPLY_TYPE_UNKNOWN = 0,
	POWER_SUPPLY_TYPE_BATTERY,
	POWER_SUPPLY_TYPE_UPS,
	POWER_SUPPLY_TYPE_MAINS,
	POWER_SUPPLY_TYPE_USB,			/* Standard Downstream Port */
	POWER_SUPPLY_TYPE_USB_DCP,		/* Dedicated Charging Port */
	POWER_SUPPLY_TYPE_USB_CDP,		/* Charging Downstream Port */
	POWER_SUPPLY_TYPE_USB_ACA,		/* Accessory Charger Adapters */
	POWER_SUPPLY_TYPE_USB_TYPE_C,		/* Type C Port */
	POWER_SUPPLY_TYPE_USB_PD,		/* Power Delivery Port */
	POWER_SUPPLY_TYPE_USB_PD_DRP,		/* PD Dual Role Port */
	POWER_SUPPLY_TYPE_APPLE_BRICK_ID,	/* Apple Charging Method */
};
4.power_supply_usb_type
enum power_supply_usb_type {
	POWER_SUPPLY_USB_TYPE_UNKNOWN = 0,
	POWER_SUPPLY_USB_TYPE_SDP,		/* Standard Downstream Port */
	POWER_SUPPLY_USB_TYPE_DCP,		/* Dedicated Charging Port */
	POWER_SUPPLY_USB_TYPE_CDP,		/* Charging Downstream Port */
	POWER_SUPPLY_USB_TYPE_ACA,		/* Accessory Charger Adapters */
	POWER_SUPPLY_USB_TYPE_C,		/* Type C Port */
	POWER_SUPPLY_USB_TYPE_PD,		/* Power Delivery Port */
	POWER_SUPPLY_USB_TYPE_PD_DRP,		/* PD Dual Role Port */
	POWER_SUPPLY_USB_TYPE_PD_PPS,		/* PD Programmable Power Supply */
	POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID,	/* Apple Charging Method */
};
5.power_supply_property

psy属性的类型

enum power_supply_property {
	/* Properties of type `int' */
	POWER_SUPPLY_PROP_STATUS = 0,
	POWER_SUPPLY_PROP_CHARGE_TYPE,
	POWER_SUPPLY_PROP_HEALTH,
	POWER_SUPPLY_PROP_PRESENT,
	POWER_SUPPLY_PROP_ONLINE,
	POWER_SUPPLY_PROP_AUTHENTIC,
	POWER_SUPPLY_PROP_TECHNOLOGY,
	POWER_SUPPLY_PROP_CYCLE_COUNT,
	POWER_SUPPLY_PROP_VOLTAGE_MAX,
	POWER_SUPPLY_PROP_VOLTAGE_MIN,
	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
	POWER_SUPPLY_PROP_VOLTAGE_NOW,
	POWER_SUPPLY_PROP_VOLTAGE_AVG,
	POWER_SUPPLY_PROP_VOLTAGE_OCV,
	POWER_SUPPLY_PROP_VOLTAGE_BOOT,
	POWER_SUPPLY_PROP_CURRENT_MAX,
	POWER_SUPPLY_PROP_CURRENT_NOW,
	POWER_SUPPLY_PROP_CURRENT_AVG,
	POWER_SUPPLY_PROP_CURRENT_BOOT,
	POWER_SUPPLY_PROP_POWER_NOW,
	POWER_SUPPLY_PROP_POWER_AVG,
	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
	POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN,
	POWER_SUPPLY_PROP_CHARGE_FULL,
	POWER_SUPPLY_PROP_CHARGE_EMPTY,
	POWER_SUPPLY_PROP_CHARGE_NOW,
	POWER_SUPPLY_PROP_CHARGE_AVG,
	POWER_SUPPLY_PROP_CHARGE_COUNTER,
	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
	POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
	POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
	POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
	POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN,
	POWER_SUPPLY_PROP_ENERGY_FULL,
	POWER_SUPPLY_PROP_ENERGY_EMPTY,
	POWER_SUPPLY_PROP_ENERGY_NOW,
	POWER_SUPPLY_PROP_ENERGY_AVG,
	POWER_SUPPLY_PROP_CAPACITY, /* in percents! */
	POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN, /* in percents! */
	POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX, /* in percents! */
	POWER_SUPPLY_PROP_CAPACITY_LEVEL,
	POWER_SUPPLY_PROP_TEMP,
	POWER_SUPPLY_PROP_TEMP_MAX,
	POWER_SUPPLY_PROP_TEMP_MIN,
	POWER_SUPPLY_PROP_TEMP_ALERT_MIN,
	POWER_SUPPLY_PROP_TEMP_ALERT_MAX,
	POWER_SUPPLY_PROP_TEMP_AMBIENT,
	POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN,
	POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX,
	POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
	POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
	POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
	POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
	POWER_SUPPLY_PROP_TYPE, /* use power_supply.type instead */
	POWER_SUPPLY_PROP_USB_TYPE,
	POWER_SUPPLY_PROP_SCOPE,
	POWER_SUPPLY_PROP_PRECHARGE_CURRENT,
	POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
	POWER_SUPPLY_PROP_CALIBRATE,
	/* Properties of type `const char *' */
	POWER_SUPPLY_PROP_MODEL_NAME,
	POWER_SUPPLY_PROP_MANUFACTURER,
	POWER_SUPPLY_PROP_SERIAL_NUMBER,
};
6.power_supply_propval

调用回调函数get_property后,用来存储获取到的结果

union power_supply_propval {
	int intval;
	const char *strval;
};
7.power_supply_config
/* Run-time specific power supply configuration */
struct power_supply_config {
	struct device_node *of_node;
	struct fwnode_handle *fwnode;

	/* Driver private data */
	void *drv_data;

	char **supplied_to;
	size_t num_supplicants;
};

2.power supply的注册函数

power_supply_register和devm_power_supply_register差不多一样,devm_power_supply_register会在驱动卸载时自动卸载psy设备

1.power_supply_register
//注册新的psy设备到驱动中  
 /**
 * power_supply_register() - Register new power supply
 * @parent:	Device to be a parent of power supply's device, usually
 *		the device which probe function calls this
 * @desc:	Description of power supply, must be valid through whole		//全局变量的psy的属性描述
 *		lifetime of this power supply
 * @cfg:	Run-time specific configuration accessed during registering,
 *		may be NULL
 *
 * Return: A pointer to newly allocated power_supply on success					//sucess:power supply 类型的psy指针
 * or ERR_PTR otherwise.														  fail:ERR_PTR + 指针	
 * Use power_supply_unregister() on returned power_supply pointer to release	//
 * resources.
 */
struct power_supply *__must_check power_supply_register(struct device *parent,
		const struct power_supply_desc *desc,
		const struct power_supply_config *cfg)
2.devm_power_supply_register
/**
 * devm_power_supply_register() - Register managed power supply
 * @parent:	Device to be a parent of power supply's device, usually
 *		the device which probe function calls this
 * @desc:	Description of power supply, must be valid through whole
 *		lifetime of this power supply
 * @cfg:	Run-time specific configuration accessed during registering,
 *		may be NULL
 *
 * Return: A pointer to newly allocated power_supply on success
 * or ERR_PTR otherwise.
 * The returned power_supply pointer will be automatically unregistered
 * on driver detach.
 */
struct power_supply *__must_check
devm_power_supply_register(struct device *parent,
		const struct power_supply_desc *desc,
		const struct power_supply_config *cfg)
{
	struct power_supply **ptr, *psy;

	ptr = devres_alloc(devm_power_supply_release, sizeof(*ptr), GFP_KERNEL);

	if (!ptr)
		return ERR_PTR(-ENOMEM);
	psy = __power_supply_register(parent, desc, cfg, true);
	if (IS_ERR(psy)) {
		devres_free(ptr);
	} else {
		*ptr = psy;
		devres_add(parent, ptr);
	}
	return psy;
}
3.power_supply_unregister

卸载psy设备

/**
 * power_supply_unregister() - Remove this power supply from system
 * @psy:	Pointer to power supply to unregister
 *
 * Remove this power supply from the system. The resources of power supply
 * will be freed here or on last power_supply_put() call.
 */
void power_supply_unregister(struct power_supply *psy)
{
	WARN_ON(atomic_dec_return(&psy->use_cnt));
	psy->removing = true;
	cancel_work_sync(&psy->changed_work);
	cancel_delayed_work_sync(&psy->deferred_register_work);
	sysfs_remove_link(&psy->dev.kobj, "powers");
	power_supply_remove_triggers(psy);
	psy_unregister_cooler(psy);
	psy_unregister_thermal(psy);
	device_init_wakeup(&psy->dev, false);
	device_unregister(&psy->dev);
}
4.power_supply_changed

当psy属性变化时,调用power_supply_changed更新psy状态,并且向应用上报状态

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;
	pm_stay_awake(&psy->dev);
	spin_unlock_irqrestore(&psy->changed_lock, flags);
	schedule_work(&psy->changed_work);
}

3.power supply其他函数

1.power_supply_get_by_name

通过psy的name返回power_supply指针

/**
 * power_supply_get_by_name() - Search for a power supply and returns its ref
 * @name: Power supply name to fetch
 *
 * If power supply was found, it increases reference count for the
 * internal power supply's device. The user should power_supply_put()
 * after usage.
 *
 * Return: On success returns a reference to a power supply with
 * matching name equals to @name, a NULL otherwise.
 */
struct power_supply *power_supply_get_by_name(const char *name)
{
	struct power_supply *psy = NULL;
	struct device *dev = class_find_device(power_supply_class, NULL, name,
					power_supply_match_device_by_name);

	if (dev) {
		psy = dev_get_drvdata(dev);
		atomic_inc(&psy->use_cnt);
	}

	return psy;
}
2.power_supply_am_i_supplied

查询自己是否由其他PSY供电

int power_supply_am_i_supplied(struct power_supply *psy)
{
	struct psy_am_i_supplied_data data = { psy, 0 };
	int error;

	error = class_for_each_device(power_supply_class, NULL, &data,
				      __power_supply_am_i_supplied);

	dev_dbg(&psy->dev, "%s count %u err %d\n", __func__, data.count, error);

	if (data.count == 0)
		return -ENODEV;

	return error;
}
3.power_supply_set_battery_charged

调用指定的PSY的set_changed回调

int power_supply_set_battery_charged(struct power_supply *psy)
{
	if (atomic_read(&psy->use_cnt) >= 0 &&
			psy->desc->type == POWER_SUPPLY_TYPE_BATTERY &&
			psy->desc->set_charged) {
		psy->desc->set_charged(psy);
		return 0;
	}

	return -EINVAL;
}
4.power_supply_is_system_supplied

检查系统中是否有有效的或者处于online状态的PSY,如果没有,可能为桌面系统、

int power_supply_is_system_supplied(void)
{
	int error;
	unsigned int count = 0;

	error = class_for_each_device(power_supply_class, NULL, &count,
				      __power_supply_is_system_supplied);

	/*
	 * If no power class device was found at all, most probably we are
	 * running on a desktop system, so assume we are on mains power.
	 */
	if (count == 0)
		return 1;

	return error;
}
5.power_supply_powers

在指定的设备的sysfs目录下创建指定的PSY符合连接。

int power_supply_powers(struct power_supply *psy, struct device *dev)
{
	return sysfs_create_link(&psy->dev.kobj, &dev->kobj, "powers");
}

4.power supply的节点注册以及消息通知

在init函数中先是在/sys/class下创建power_supply目录后,注册uevent回调还有初始化节点,但是这边有个疑问这些节点是如何注册进去的,调用power_supply_register的时候有指定了power_supply_desc *desc这个参数,在这个结构体中properties指定相应的类型后就会注册相应的节点

subsys_initcall(power_supply_class_init);
static int __init power_supply_class_init(void)
{
	power_supply_class = class_create(THIS_MODULE, "power_supply");

	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;
}

void power_supply_init_attrs(struct device_type *dev_type)
{
	int i;

	dev_type->groups = power_supply_attr_groups;

	for (i = 0; i < ARRAY_SIZE(power_supply_attrs); i++)
		__power_supply_attrs[i] = &power_supply_attrs[i].attr;
}

正常的流程是注册psy设备 --> 通过申请中断或者队列进行power_supply_change–>系统会发送uevent信息给应用—>应用对uevent信息进行解析

5.项目的adc_battery驱动

#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/iio/iio.h>
#include <linux/iio/consumer.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/timer.h>

#define ADC_BATTERY_STATE	"bat-gpios"
#define BATTERY_JITTER_DELAY 500	//500ms

struct battery_data {
	struct power_supply	*psy;
	struct iio_channel 	*raw_val;
	struct device *dev;
	struct gpio_desc *bat_state;
	struct delayed_work adc_battery_work;
	//struct mutex lock;
	int online;
	int diff_voltage;
	int capacity;
};

static enum power_supply_property adc_battery_props[] = {
	//POWER_SUPPLY_PROP_PRESENT,
	//POWER_SUPPLY_PROP_ONLINE,
	POWER_SUPPLY_PROP_STATUS,
	POWER_SUPPLY_PROP_VOLTAGE_NOW,
	//POWER_SUPPLY_PROP_CURRENT_NOW,
	//POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
	//POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
	//POWER_SUPPLY_PROP_HEALTH,
	//POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
	//POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
	POWER_SUPPLY_PROP_CAPACITY,
};

static int battery_get_capacity(struct battery_data *bat, int *capacity)
{
	int voltage;
	int diff_voltage;
	int count = 0;
	int temp[32] = {0}, sum = 0;

	for(count = 0; count < 16; count++)
	{
		iio_read_channel_raw(bat->raw_val, &voltage);
		temp[count] = voltage;
		sum += temp[count];
	}
	voltage = sum / 16;

	diff_voltage = (1800 * voltage) / 1023;
	*capacity = (47900 -(163600 - diff_voltage * 100)) / 479;

	return 0;
}

static int battery_get_voltage(struct battery_data *bat, int *voltage)
{
	int read_voltage = 0;
	int count = 0, temp[32] = {0}, sum = 0;

	for(count = 0; count < 16; count++)
	{
		iio_read_channel_raw(bat->raw_val, &read_voltage);
		temp[count] = read_voltage;
		sum += temp[count];
	}
	read_voltage = sum / 16;

	*voltage = (1800 * read_voltage) / 1023;

	return 0;
}
static int adc_battery_get_prop(struct power_supply *psy,
					     enum power_supply_property psp,
					     union power_supply_propval *val)
{
	struct battery_data *bat = power_supply_get_drvdata(psy);
	int state;
	int capacity = 0;
	int voltage = 0;

	//mutex_lock(&bat->lock);
	state = bat->online;
	//mutex_unlock(&bat->lock);

	switch(psp)
	{
		case POWER_SUPPLY_PROP_STATUS:
			if(state)
				val->intval = POWER_SUPPLY_STATUS_CHARGING;
			else
				val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
			break;
		case POWER_SUPPLY_PROP_CAPACITY:
			battery_get_capacity(bat, &capacity);
			val->intval = capacity;
			break;
		case POWER_SUPPLY_PROP_VOLTAGE_NOW:
			battery_get_voltage(bat, &voltage);
			val->intval = voltage;
			break;
		default:
			return -EINVAL;
	}

	return 0;
}

static const struct power_supply_desc adc_battery_supply_desc = {
	.name = "adc-battery",
	.type = POWER_SUPPLY_TYPE_BATTERY,
	.properties = adc_battery_props,
	.num_properties = ARRAY_SIZE(adc_battery_props),
	.get_property = adc_battery_get_prop,
	//.set_property = adc_battery_set_prop,
};

static irqreturn_t battery_irq_handler_thread(int irq, void *private)
{

	struct battery_data *bat = private;

	schedule_delayed_work(&bat->adc_battery_work,
		msecs_to_jiffies(BATTERY_JITTER_DELAY));

	return IRQ_HANDLED;
}

static void adc_battery_work_fun(struct work_struct *work)
{
	struct delayed_work *dwork = to_delayed_work(work);
	struct battery_data *bat = container_of(dwork, struct battery_data, adc_battery_work);
	int state = 0;
	int val = 0;
	int voltage;
	int capacity;

	val = gpiod_get_value(bat->bat_state);
	state = bat->online;
	if(val != state)
	{
		battery_get_voltage(bat, &voltage);
		battery_get_capacity(bat, &capacity);
		//mutex_lock(&bat->lock);
		bat->online = val;
		bat->diff_voltage = voltage;
		bat->capacity = capacity;
		//mutex_unlock(&bat->lock);
	}
	power_supply_changed(bat->psy);

}

static int adc_battery_probe(struct platform_device *pdev)
{
	struct battery_data *pdata;
	struct power_supply_config psy_cfg = {};
	int ret = 0;

	printk("%s %d", __func__, __LINE__);
	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
	if(!pdata)
		return -ENOMEM;

	pdata->dev = &pdev->dev;
	//mutex_init(&pdata->lock);

	pdata->raw_val = iio_channel_get(&pdev->dev, NULL);
	if(IS_ERR(pdata->raw_val))
	{
		if(PTR_ERR(pdata->raw_val) == -ENODEV)
			return -EPROBE_DEFER;
		printk("%s %d:no raw_val find\n", __func__, __LINE__);
		return PTR_ERR(pdata->raw_val);
	}

	pdata->bat_state = devm_gpiod_get_optional(&pdev->dev, "bat-state", GPIOD_IN);
	if (IS_ERR(pdata->bat_state)) {
		printk("Could not probe bat state pin.\n");
		return PTR_ERR(pdata->bat_state);
	}

	//default status and diff_voltage
	pdata->online = gpiod_get_value(pdata->bat_state);
	battery_get_voltage(pdata, &pdata->diff_voltage);
	battery_get_capacity(pdata, &pdata->capacity);
	platform_set_drvdata(pdev, pdata);

	INIT_DELAYED_WORK(&pdata->adc_battery_work, adc_battery_work_fun);

	ret = devm_request_threaded_irq(&pdev->dev, gpiod_to_irq(pdata->bat_state), NULL,
					battery_irq_handler_thread,
					IRQF_TRIGGER_FALLING |IRQF_TRIGGER_RISING |IRQF_ONESHOT,
					ADC_BATTERY_STATE, pdata);
	if (ret)
	{
		printk("request irq thread fail\n");
		return ret;
	}

	psy_cfg.drv_data = pdata;
	psy_cfg.of_node = pdev->dev.of_node;

	pdata->psy = devm_power_supply_register(&pdev->dev,
		&adc_battery_supply_desc,
		&psy_cfg);
	if (IS_ERR(pdata->psy)) {
		dev_err(&pdev->dev, "failed to register power supply: %ld\n",
			PTR_ERR(pdata->psy));
		goto ungister_psy;
	}

	schedule_delayed_work(&pdata->adc_battery_work,
		msecs_to_jiffies(BATTERY_JITTER_DELAY));

	printk("%s:%d:probe sucess\n", __func__, __LINE__);

	return 0;

ungister_psy:
	power_supply_unregister(pdata->psy);
	return ret;

}

static int adc_battery_remove(struct platform_device *pdev)
{
	struct battery_data *bat = platform_get_drvdata(pdev);

	power_supply_unregister(bat->psy);

	return 0;
}

#ifdef CONFIG_OF
static const struct of_device_id ac_battery_id_of_match[] = {
	{.compatible = "adc-battery", },
	{}
};
MODULE_DEVICE_TABLE(of, ac_battery_id_of_match);
#endif

static struct platform_driver adc_battery_driver = {
	.probe    = adc_battery_probe,
	.remove	  = adc_battery_remove,
	.driver   = {
		.name  = "adc-battery-power-supply",
		.of_match_table = of_match_ptr(ac_battery_id_of_match),
	},
};

module_platform_driver(adc_battery_driver);

MODULE_AUTHOR("nano");
MODULE_DESCRIPTION("Battery power supply driver for adc battery");
MODULE_LICENSE("GPL");

6.应用uevent的接收

1.创建uevent socket

int uevent_open_socket(int buf_size, bool passcred)
{
	int sock_fd;
	struct sockaddr_nl addr;
	int on = passcred;
	
	memset(&addr, 0, sizeof(addr));
    addr.nl_family = AF_NETLINK;
    addr.nl_pid = getpid();
    addr.nl_groups = 0xffffffff;
    
	sock_fd = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT);
	if(socket_fd < 0)
		return -1;

    //设置套接字接收缓冲区大小
	if(setsockopt(sock_fd, SOL_SOCKET, SO_RCVBUF, &buf_size, sizeof(buf_size)) < 0)
	{
		close(sock_fd);
		return -1;
	}
    //获取已连接 AF_UNIX 流套接字的 pid/uid/gid 的简单方法, SO_PEERCRED检索对等进程的凭据,而无需来自对等进程的任何交互。相比之下,SCM_CREDENTIALS是一种发送/接收对等进程凭据的机制,然后由内核检查。当进程以 UID 0 运行时,这种细微差别可能很重要
	setsockopt(sock_fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
	
	if(bind(sock_addr, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
        close(s);
        return -1;
    }
    reuturn sock_fd;
}

2.初始化uevent_init

static int  epollfd;
int uevent_init(void (*handler)(uint32_t))
{
    struct epoll_event event;
    
    event.events = EPOLLIN;
    event.data.ptr = (void*)handler;
    
    epollfd = epoll_create(100);
    if(epollfd == -1)
    {
        printf("create epollfd fail\n");
        return -1;
    }
    int uevent_fd = uevent_open_socket(4096, true);
    if(uevent_fd < 0)
        printf("open socket fail\n");
    
    //设置非阻塞
    fcntl(uevent_fd, F_SETFD, O_NONBLOCK);
    if(epoll_ctl(epollfd, EPOLL_CTL_ADD, sock_fd, &event) == -1)
    {
        printf("epoll ctl fail\n");
        return -1;
    }
    return 0;
}
uevent_init(uevent_event);

static void uevent_event(uint32_t /*epevents*/) {
    char msg[UEVENT_MSG_LEN+2];
    char *cp;
    int n;

    n = uevent_kernel_multicast_recv(uevent_fd, msg, UEVENT_MSG_LEN);
    if (n <= 0)
        return;
    if (n >= UEVENT_MSG_LEN)   /* overflow -- discard */
        return;

    msg[n] = '\0';
    msg[n+1] = '\0';
    cp = msg;

    while (*cp) {
        if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) {
            healthd_battery_update();
            break;
        }

        /* advance to after the next \0 */
        while (*cp++)
            ;
    }
}
ssize_t uevent_kernel_multicast_recv(int socket, void *buffer, size_t length)
{
    uid_t uid = -1;
    return uevent_kernel_multicast_uid_recv(socket, buffer, length, &uid);
}
ssize_t uevent_kernel_multicast_uid_recv(int socket, void *buffer, size_t length, uid_t *uid)
{
    return uevent_kernel_recv(socket, buffer, length, true, uid);
}
ssize_t uevent_kernel_recv(int socket, void *buffer, size_t length, bool require_group, uid_t *uid)
{
    struct iovec iov = { buffer, length };
    struct sockaddr_nl addr;
    char control[CMSG_SPACE(sizeof(struct ucred))];
    struct msghdr hdr = {
        &addr,
        sizeof(addr),
        &iov,
        1,
        control,
        sizeof(control),
        0,
    };

    *uid = -1;
    ssize_t n = recvmsg(socket, &hdr, 0);
    if (n <= 0) {
        return n;
    }

    struct cmsghdr *cmsg = CMSG_FIRSTHDR(&hdr);
    if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
        /* ignoring netlink message with no sender credentials */
        goto out;
    }

    struct ucred *cred = (struct ucred *)CMSG_DATA(cmsg);
    *uid = cred->uid;
    if (cred->uid != 0) {
        /* ignoring netlink message from non-root user */
        goto out;
    }

    if (addr.nl_pid != 0) {
        /* ignore non-kernel */
        goto out;
    }
    if (require_group && addr.nl_groups == 0) {
        /* ignore unicast messages when requested */
        goto out;
    }

    return n;

out:
    /* clear residual potentially malicious data */
    bzero(buffer, length);
    errno = EIO;
    return -1;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值