1. 程式人生 > >linux裝置驅動uevent詳解,高通平臺battery上報電量例項

linux裝置驅動uevent詳解,高通平臺battery上報電量例項

本文以高通平臺上的android系統為參照展開探討。

1,uevent是什麼呢?
uevent是一種linux裝置模型中的一個組成部分。kset中包含的uevent_ops結構體擁有uevent的操作函式。
uevent可以在裝置發生變化時主動通知應用層。是對普通先註冊裝置後註冊驅動模式的一種補充。一般用作usb裝置的自動驅動載入、電池電量上報等。

2,uevent主動通知應用層的原理是什麼?
有兩種方式,第一種是設定環境變數後使用call_usermodehelper_setup函式直接呼叫應用層程式;第二種是通過netlink嚮應用層傳送訊息,在應用層的守護程序收到訊息後完成相關操作。其中第一種本方式較少使用,以第二種為主。
ps:netlink是一種基於socket的核心空間與使用者空間的雙向通訊機制,十分靈活好用。

3,sys目錄下有許多uevent節點,它們是幹什麼的?

最開始我對uevent的理解感到困惑就是因為這些uevent節點,如cat /sys/devices/soc/qpnp-smbcharger-17/power_supply/battery/uevent節點可以檢視到電池狀態,充放電電流等等資訊,好像uevent就像其他sysfs中的節點一樣僅僅供上層讀取。然而這並不是uevent的全部,在上面解釋的uevent機制中是完全不需要這些節點存在的,事實上上層也並不會去使用這些節點。這些節點的存在僅僅是為了除錯目的,可以提供一種簡單方式檢測到uevent的中間資訊。但在android的電源管理結構中電池的相關資訊是通過讀取/sys/devices/soc/qpnp-smbcharger-17/power_supply/battery/uevent節點獲取的,netlink訊息只發出KOBJ_CHANGE的action kobject_uevent(&cm->dev->kobj, KOBJ_CHANGE),去觸發上層讀取sys節點。
注意:這些uevent節點與/dev/input/eventX節點雖然名字相似,但其原理和作用是完全不同的。eventX節點是輸入子系統的事件上報介面,需要上層來讀取eventX節點。

4,使用uevent上報電量的例項分析

kernel\drivers\power\qpnp-smbcharger.c
電量狀態變化的中斷函式中呼叫smbchg_charging_status_change函式
static int smbchg_charging_status_change(struct smbchg_chip *chip)
{
	smbchg_vfloat_adjust_check(chip);
	set_property_on_fg(chip, POWER_SUPPLY_PROP_STATUS,
			get_prop_batt_status(chip));
	return 0;
}
set_property_on_fg原始碼位於
kernel\drivers\power\qpnp-smbcharger.c
static int set_property_on_fg(struct smbchg_chip *chip,
		enum power_supply_property prop, int val)
{
	int rc;
	union power_supply_propval ret = {0, };


	if (!chip->bms_psy && chip->bms_psy_name)
		chip->bms_psy =
			power_supply_get_by_name((char *)chip->bms_psy_name);
	if (!chip->bms_psy) {
		pr_smb(PR_STATUS, "no bms psy found\n");
		return -EINVAL;
	}


	ret.intval = val;
	rc = chip->bms_psy->set_property(chip->bms_psy, prop, &ret);
	if (rc)
		pr_smb(PR_STATUS,
			"bms psy does not allow updating prop %d rc = %d\n",
			prop, rc);


	return rc;
}
set_property指標賦值位於qpnp-charger.c的prob函式中
static int qpnp_charger_probe(struct spmi_device *spmi)
{

	chip->batt_psy.set_property = qpnp_batt_power_set_property;
}

qpnp_batt_power_set_property函式的實現程式碼位置:kernel\drivers\power\qpnp-charger.c

此函式用於設定uevent節點,並且發出netlink訊息通知上層讀取

static int qpnp_batt_power_set_property(struct power_supply *psy,
				  enum power_supply_property psp,
				  const union power_supply_propval *val)
{
	struct qpnp_chg_chip *chip = container_of(psy, struct qpnp_chg_chip,
								batt_psy);
	int rc = 0;


	switch (psp) {
	case POWER_SUPPLY_PROP_COOL_TEMP:
		rc = qpnp_chg_configure_jeita(chip, psp, val->intval);
		break;
	case POWER_SUPPLY_PROP_WARM_TEMP:
		rc = qpnp_chg_configure_jeita(chip, psp, val->intval);
		break;
	case POWER_SUPPLY_PROP_CAPACITY:
		chip->fake_battery_soc = val->intval;	//將電量值賦值到fake_battery_soc變數
		power_supply_changed(&chip->batt_psy);	//呼叫power_supply_changed傳送netlink訊息,通知應用層重新讀取
		break;
	...
	default:
		return -EINVAL;
	}


	pr_debug("psy changed batt_psy\n");
	power_supply_changed(&chip->batt_psy);
	return rc;
}
power_supply_changed(&chip->batt_psy);原始碼位於kernel\drivers\power\power_supply_core.c
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);
	/*
	 * Check 'changed' here to avoid issues due to race between
	 * power_supply_changed() and this routine. In worst case
	 * power_supply_changed() can be called again just before we take above
	 * lock. During the first call of this routine we will mark 'changed' as
	 * false and it will stay false for the next call as well.
	 */
	if (likely(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);
		atomic_notifier_call_chain(&power_supply_notifier,
				PSY_EVENT_PROP_CHANGED, psy);
		kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE);
		spin_lock_irqsave(&psy->changed_lock, flags);
	}


	/*
	 * Hold the wakeup_source until all events are processed.
	 * power_supply_changed() might have called again and have set 'changed'
	 * to true.
	 */
	if (likely(!psy->changed))
		pm_relax(psy->dev);
	spin_unlock_irqrestore(&psy->changed_lock, flags);
}
其中kobject_uevent即傳送netlink訊息到上層kernel\lib\kobject_uevent.c
int kobject_uevent(struct kobject *kobj, enum kobject_action action)
{
	return kobject_uevent_env(kobj, action, NULL);
}
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
		       char *envp_ext[])
{
	struct kobj_uevent_env *env;
	const char *action_string = kobject_actions[action];
	const char *devpath = NULL;
	const char *subsystem;
	struct kobject *top_kobj;
	struct kset *kset;
	const struct kset_uevent_ops *uevent_ops;
	int i = 0;
	int retval = 0;


	...
#if defined(CONFIG_NET)
	/* send netlink message */
	list_for_each_entry(ue_sk, &uevent_sock_list, list) {
		struct sock *uevent_sock = ue_sk->sk;
		struct sk_buff *skb;
		size_t len;


		if (!netlink_has_listeners(uevent_sock, 1))
			continue;


		/* allocate message with the maximum possible size */
		len = strlen(action_string) + strlen(devpath) + 2;
		skb = alloc_skb(len + env->buflen, GFP_KERNEL);
		if (skb) {
			char *scratch;


			/* add header */
			scratch = skb_put(skb, len);
			sprintf(scratch, "%[email protected]%s", action_string, devpath);


			/* copy keys to our continuous event payload buffer */
			for (i = 0; i < env->envp_idx; i++) {
				len = strlen(env->envp[i]) + 1;
				scratch = skb_put(skb, len);
				strcpy(scratch, env->envp[i]);
			}


			NETLINK_CB(skb).dst_group = 1;
			retval = netlink_broadcast_filtered(uevent_sock, skb,	//傳送netlink訊息
							    0, 1, GFP_KERNEL,
							    kobj_bcast_filter,
							    kobj);
			/* ENOBUFS should be handled in userspace */
			if (retval == -ENOBUFS || retval == -ESRCH)
				retval = 0;
		} else
			retval = -ENOMEM;
	}
#endif
	mutex_unlock(&uevent_sock_mutex);


}


相關推薦

linux裝置驅動uevent平臺battery上報電量例項

本文以高通平臺上的android系統為參照展開探討。1,uevent是什麼呢? uevent是一種linux裝置模型中的一個組成部分。kset中包含的uevent_ops結構體擁有uevent的操作函式。 uevent可以在裝置發生變化時主動通知應用層。是對普通先註冊裝置後

分享《Linux裝置驅動開發》第2版清電子版

新浪微博:@宋寶華Barry 在@微盤 分享了《linux裝置驅動開發詳解》第2版1080P電子版,擬升級為第3版,3.16核心,Cortex-A9 SMP,Device tree, DVFS, suspend/hibernation, big.LITTLE, CMA,分層/

Linux裝置驅動開發 第3版 (即 Linux裝置驅動開發 基於最新的Linux 4 0核心 )進展同步更

                本博實時更新《Linux裝置驅動開發詳解(第3版)》的最新進展。 目前已經完成稿件。 2015

Linux裝置驅動開發》-- 互斥體(mutex)

儘管訊號量已經可以實現互斥的功能,而且包含 DECLARE_MUTEX() 、init_MUTEX ()等定義訊號量的巨集或函式, 從名字上看就體現出了互斥體的概念, 但是mutex 在 Linux 核心中還是真實地存

linux裝置驅動開發》筆記——15 linux i2c驅動

  結合實際程式碼和書中描述,可能跟書上有一定出入。本文後續晶片相關程式碼參考ZYNQ。 15.1 總體結構   如下圖,i2c驅動分為如下幾個重要模組 核心層core,完成i2c匯流排、裝置、驅動模型,對使用者提供sys檔案系統訪問支援;為i2c內部adpter等提供註冊介面。  adpter,介面卡,實

宋寶華《Linux裝置驅動開發》——sysfs檔案系統與linux裝置模型(5.4.2)

以下讀書筆記內容,摘自宋寶華《Linux裝置驅動開發詳解》一書。 1、sysfs檔案系統的簡介 (1)linux2.6以後的核心引進syfs檔案系統,是虛擬檔案系統; (2)產生一個包括所有系統硬體

關於召回《Linux裝置驅動開發-基於最新的Linux 4.0核心》的通知

問題描述關於《Linux裝置驅動開發詳解:基於最新的Linux 4.0核心》一書1.華章分社在沒

LinuxLinux裝置驅動開發:基於最新的Linux 4.0核心

1 Linux裝置驅動概述及開發環境構建 1.1 裝置驅動的作用 驅使硬體裝置行動 1.2 無作業系統時的裝置驅動 典型架構:一個無限迴圈中夾雜著對裝置中斷的檢測或者對裝置的輪詢 1.3 有作業系統時的裝置驅動 併發 、記

Linux裝置驅動開發:基於最新的Linux 4.0核心》一刷勘誤

這是第一次印刷的勘誤,大部分應該買的都是5刷了,這些錯誤基本已經絕跡。還是有部分童鞋買的書老書,

宋寶華《linux裝置驅動開發》——platform裝置驅動(12.2)

以下讀書筆記,整理於宋寶華《linux裝置驅動開發詳解》一書。 1、piatform匯流排出現的原因 在SOC整合的獨立外設控制器、掛接在soc記憶體空間的外設不依附與此類匯流排(PCI、USB、I

linux平臺裝置驅動架構 Linux Platform Device and Driver——神文非常詳細

從Linux 2.6起引入了一套新的驅動管理和註冊機制:Platform_device和Platform_driver。 Linux中大部分的裝置驅動,都可以使用這套機制, 裝置用Platform_device表示,驅動用Platform_driver進行註冊。 Linux platform driver機

Linux驅動Linux裝置樹語法

1 概念Linux核心從3.x開始引入裝置樹的概念,用於實現驅動程式碼與裝置資訊相分離。在裝置樹出現以前,所有關於裝置的具體資訊都要寫在驅動裡,一旦外圍裝置變化,驅動程式碼就要重寫。引入了裝置樹之後,驅動程式碼只負責處理驅動的邏輯,而關於裝置的具體資訊存放到裝置樹檔案中,這樣,如果只是硬體介面資訊的變化而沒有

轉載:linux平臺裝置驅動架構 Linux Platform Device and Driver

從Linux 2.6起引入了一套新的驅動管理和註冊機制:Platform_device和Platform_driver。Linux中大部分的裝置驅動,都可以使用這套機制, 裝置用Platform_device表示,驅動用Platform_driver進行註冊。Linux platform driver機制和傳

《Linux4.0裝置驅動開發》筆記--第十八章:ARM Linux裝置

18.1 ARM裝置樹簡介 裝置舒適一種描述印鑑的資料結構,它起源於OpenFirmware(OF) 採用裝置樹前後對比: 採用裝置樹之前:ARM架構的板極硬體細節過多的被硬編碼在arch/arm/plat-xxx和arch/arm/mach-xxx中

《Linux4.0裝置驅動開發》筆記--第二章:Linux核心及核心程式設計

2.1 Linux核心發展及演變 1991年10月5日 Linus Torvalds建立 五個支柱:Unix系統、Minix系統、GNU計劃、POSIX標準和Internet 每2-3個月更新一次大的版本號 2.2 Linux2.6後的核心特點

Linux裝置樹語法【轉】

轉自:https://www.cnblogs.com/xiaojiang1025/p/6131381.html 概念 Linux核心從3.x開始引入裝置樹的概念,用於實現驅動程式碼與裝置資訊相分離。在裝置樹出現以前,所有關於裝置的具體資訊都要寫在驅動裡,一旦外圍裝置變化,驅動程式碼就要重寫。引入了裝置樹之

Linux裝置樹語法

概念 Linux核心從3.x開始引入裝置樹的概念,用於實現驅動程式碼與裝置資訊相分離。在裝置樹出現以前,所有關於裝置的具體資訊都要寫在驅動裡,一旦外圍裝置變化,驅動程式碼就要重寫。引入了裝置樹之後,驅動程式碼只負責處理驅動的邏輯,而關於裝置的具體資訊存放到裝置樹檔案中,

Linux按鍵驅動程式設計---從簡單到不簡單

混雜裝置驅動模型: 1. 混雜裝置描述         在Linux系統中,存在一類字元裝置,它們擁有相同的主裝置號(10),單次裝置號不同,我們稱這類裝置為混            雜裝置(miscdevice).所有的混雜裝置形成一個連結串列,對裝置訪問時核心根據次裝置

LINUX裝置驅動程式(第3版)[清PDF]

下載地址:網盤下載 內容簡介 編輯 《LINUX裝置驅動程式(第3版)》已針對Linux核心的2610版本徹底更新過了。核心的這個版本針對常見任務完成了合理化設計及相應的簡化,如即插即用、利用sysfs檔案系統和使用者空間互動,以及標準總線上的多裝置管理等等

FS_S5PC100平臺上Linux Camera驅動開發(一)

說明:         理解攝像頭驅動需要四個前提:         1)攝像頭基本的工作原理和S5PC100整合的 Camera控制器 的工作原理         2)platform_device和platform_driver工作原理         3)Linu