Android input epoll/inotify机制
power supply是如何上报电池信息的
Android :kernel uevent发送(热插拔)事件到用户空间
源码分析
Kernel space
kernel/msm-4.19/drivers/power/supply/power_supply_core.c
kernel/msm-4.19/drivers/power/supply/power_supply_sysfs.c
power_supply_class_init
power_supply_class->dev_uevent = power_supply_uevent;
初始化workqueue,后续用于调度
INIT_WORK(&psy->changed_work, power_supply_changed_work);
在驱动中检测到硬件状态发生变化会调用power_supply_changed,进而调取workqueue
schedule_work(&psy->changed_work);
添加环境变量,回调keset中注册的power_supply_uevent,将msg以socketbuffer的格式进行打包
power_supply_changed_work
kobject_uevent(&psy->dev.kobj, KOBJ_CHANGE);
kobject_uevent_env(struct kobject *kobj, enum kobject_action action, char *envp_ext[])
if(envp_ext != NULL)
add_uevnet_var(env, "%s", envp_ext[i]);//测试用例通过这种envp_ext的方式来实现
if (uevent_ops && uevent_ops->uevent) //uevent_ops = kset->uevent_ops; uevent_ops->uevent = dev_uevent
uevent_ops->uevent(kset, kobj, env); //power_supply_class->dev_uevent = power_supply_uevent; class类kset
power_supply_uevent
add_uevent_var(env, "ACTION=%s", action_string);
add_uevent_var(env, "DEVPATH=%s", devpath);
add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
kobject_uevent_net_broadcast(kobj, env, action_string, devpath);
alloc_uevent_skb // socket buffer的放置有关 header:action@devpath + socket_buffer
scratch = skb_put(skb, len); /* add header*/
sprintf(scratch, "%s@%s", action_string, devpath)
skb_put_data(skb, env->buf, env->buflen);
User space
hardware/interfaces/health/utils/libhealthloop/HealthLoop.cpp
power_supply通过调用kobject_uevent, envp_ext为NULL, 会回调class的dev_uevent并且使用的是默认的add_uevent_var
ACTION=action_string DEVPATH=devpath SUBSYSTEM=subsystem,电池上层接受的时候会通过SUBSYSTEM进行过滤
StartLoop
epollfd_.reset(epoll_create1(EPOLL_CLOEXEC)); //进程被替换时会关闭文件描述符
uevent_fd_.reset(uevent_open_socket(64 * 1024, true)); // uevent_fd_
ev.events = EPOLLIN; //1. 新的请求 2.接收到普通数据(缓冲未满) 3.正常关闭连接
ev.events |= EPOLLWAKEUP; //1. 唤醒源,系统会保持唤醒
epoll_ctl(epollfd_, EPOLL_CTL_ADD, uevent_fd_, &ev);//ADD表示绑定事件
MainLoop -> while(1)
epoll_wait(epollfd_, events, eventct, timeout);//epoll等待uevent事件
uevent_kernel_multicast_recv //接受uevent事件
strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM) //判断subsystem,battery=power_supply
ScheduleBatteryUpdate(); //更新上报电池细节(电量,健康状态等) 以'\0'作为间隔,见log
测试用例
Kernel space
Makefile
obj-y += uevent_test.o
uevent_test.c
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/semaphore.h>
#include <linux/mutex.h>
#include <linux/syscalls.h>
#include <asm/unistd.h>
#include <asm/uaccess.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/device.h>
#include <linux/kdev_t.h>
#include <linux/i2c.h>
#include <linux/input.h>
struct device *dev = NULL;
char * mesg[2];
static ssize_t power_supply_changed(struct device *dev,struct device_attribute *attr, const char *buf, size_t count)
{
char s_buf[100]={0};
mesg[0] =s_buf;
mesg[1] = NULL;
strcat(s_buf, buf);
pr_err( "jerry echo buf=%s s_buf=%s count=%d\n",buf,s_buf,count);
kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, mesg);
return count;
}
static DEVICE_ATTR(capabitlity, S_IRUGO|S_IWUSR, NULL, power_supply_changed);
static const struct attribute *kobject_event_attr[] = {
&dev_attr_capabitlity.attr,
NULL,
};
static const struct attribute_group kobject_event_attr_group = {
.attrs = (struct attribute **) kobject_event_attr,
};
static struct class kobject_event_class = {
.name = "uevent_class",
.owner = THIS_MODULE,
};
static int __init uevent_test_init(void)
{
int ret = 0;
ret = class_register(&kobject_event_class);
if (ret < 0) {
pr_err("jerry: class_register fail");
return ret;
}
dev = device_create(&kobject_event_class, NULL, MKDEV(0, 0), NULL, "uevent_test");
if (dev) {
ret = sysfs_create_group(&dev->kobj, &kobject_event_attr_group);
if(ret < 0) {
pr_err("uevent_test:sysfs_create_group fail");
return ret;
}
} else {
pr_err("uevent_test:device_create fail");
ret = -1;
return ret;
}
return 0;
}
static __exit void uevent_test_exit(void)
{
device_destroy(&kobject_event_class, MKDEV(0,0));
class_destroy(&kobject_event_class);
sysfs_remove_group(&dev->kobj, &kobject_event_attr_group);
}
module_init(uevent_test_init);
module_exit(uevent_test_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("jerry");
User space
#include <fcntl.h>
#include <string.h>
#include <utils/Log.h>
#include <cutils/uevent.h>
#define UEVENT_MSG_LEN 2048
//hardware/interfaces/health/utils/libhealthloop/HealthLoop.cpp
int main()
{
char msg[UEVENT_MSG_LEN + 2];
int n, uevent_fd_;
char* cp;
uevent_fd_ = uevent_open_socket(64*1024, true);
if (uevent_fd_ < 0) {
ALOGE( "jerry uevent_init: uevent_open_socket failed\n");
return 0;
}
while(1){
n = uevent_kernel_multicast_recv(uevent_fd_, msg, UEVENT_MSG_LEN);
if(n < 0 || n >= UEVENT_MSG_LEN ){
ALOGE("jerry failed to recevie uevent");
return 0;
}
msg[n] = '\0';
msg[n+1] = '\0';
cp = msg;
ALOGD("jerry get the uevent size n = %d, msg = %s", n, cp);
while (*cp) {
ALOGD("jerry receive the msg = %s", cp);
/* advance to after the next \0 */
while (*cp++)
;
}
}
return 0;
}
Logcat log
cd /sys/devices/virtual/uevent_class/uevent_test or
cd /sys/class/uevent_class/uevent_test
echo "uevent_test" > capabitlity
E : jerry echo buf=uevent_test
D uevent_test: jerry get the uevent size n = 161, msg = change@/devices/virtual/uevent_class/uevent_test
D uevent_test: jerry receive the msg = change@/devices/virtual/uevent_class/uevent_test
D uevent_test: jerry receive the msg = ACTION=change
D uevent_test: jerry receive the msg = DEVPATH=/devices/virtual/uevent_class/uevent_test
D uevent_test: jerry receive the msg = SUBSYSTEM=uevent_class
D uevent_test: jerry receive the msg = uevent_test
D uevent_test: jerry receive the msg = SEQNUM=8695
power_supply battery
D uevent_test: jerry receive the msg = change@/devices/platform/soc/1c40000.qcom,spmi/spmi-0/spmi0-02/1c40000.qcom,spmi:qcom,pmi632@2:qcom,qpnp-smb5/power_supply/battery
D uevent_test: jerry receive the msg = ACTION=change
D uevent_test: jerry receive the msg = DEVPATH=/devices/platform/soc/1c40000.qcom,spmi/spmi-0/spmi0-02/1c40000.qcom,spmi:qcom,pmi632@2:qcom,qpnp-smb5/power_supply/battery
D uevent_test: jerry receive the msg = SUBSYSTEM=power_supply
D uevent_test: jerry receive the msg = POWER_SUPPLY_NAME=battery
D uevent_test: jerry receive the msg = POWER_SUPPLY_INPUT_SUSPEND=0
D uevent_test: jerry receive the msg = POWER_SUPPLY_STATUS=Full
...
D uevent_test: jerry receive the msg = POWER_SUPPLY_FCC_STEPPER_ENABLE=0
D uevent_test: jerry receive the msg = SEQNUM=8694