美文网首页
Android Uevent 结合 Kernel Switch

Android Uevent 结合 Kernel Switch

作者: Nothing_655f | 来源:发表于2020-09-12 16:34 被阅读0次

    一、Kernel Switch Class

    先看看节点下有什么东西

     # ls -l /sys/class/switch/                                         
    total 0
    lrwxrwxrwx 1 root root 0 2015-01-01 08:00 hdmirx_hpd -> ../../devices/virtual/switch/hdmirx_hpd
    lrwxrwxrwx 1 root root 0 2015-01-01 08:00 video_layer1 -> ../../devices/virtual/switch/video_layer1
    # cd /sys/class/switch 
    # ls -al hdmirx_hpd/                               
    total 0
    drwxr-xr-x 3 root root    0 2015-01-01 08:00 .
    drwxr-xr-x 6 root root    0 2015-01-01 08:00 ..
    -r--r--r-- 1 root root 4096 2020-09-12 15:23 name
    drwxr-xr-x 2 root root    0 2015-01-01 08:00 power
    -r--r--r-- 1 root root 4096 2020-09-12 15:23 state
    lrwxrwxrwx 1 root root    0 2020-09-12 15:23 subsystem -> ../../../../class/switch
    -rw-r--r-- 1 root root 4096 2015-01-01 08:00 uevent
    # cat hdmirx_hpd/name                              
    hdmirx_hpd
    # cat hdmirx_hpd/state                             
    0
    

    这里简单可以看到起数据结构,我们要使用的就是 name 和 state, switch dev 的数据结构定义如下

    1. struct switch_dev {
    2. const char    *name;
    3. struct device    *dev;   
    4. int        index;
    5. int        state;
    6. ssize_t    (*print_name)(struct switch_dev *sdev, char *buf);
    7. ssize_t    (*print_state)(struct switch_dev *sdev, char *buf);
    8. };
    

    1、switch class 用法demo

    先看一个switch class 用法的简单demo

    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/switch.h>
    MODULE_LICENSE("Dual BSD/GPL");
    
    struct switch_dev sdev;
    
    static int hello_init(void)
    {
        int state, ret;
        
        printk(KERN_ALERT "Hello, world\n");
    
        sdev.name = "hello";
        ret = switch_dev_register(&sdev);
        if (ret < 0)
            return ret;
        state = 0;
        switch_set_state(&sdev, state);
    
        state = 1;
        switch_set_state(&sdev, state);
        return 0;
    }
    static void hello_exit(void)
    {
        printk(KERN_ALERT "Goodbye, world\n");
    }
    module_init(hello_init);
    module_exit(hello_exit);
    

    上面这个例子中就使用了 switch_set_state(&sdev, state); 这个方法修改了state的值

    2、switch class 函数介绍

    看看一开始的 switch_dev_register(&sdev);做了什么

    int switch_dev_register(struct switch_dev *sdev)
    {
        int ret;
        if (!switch_class) {
            ret = create_switch_class();     //如果没有switch类,则创建一个。
        if (ret < 0)
            return ret;
        }
        sdev->index = atomic_inc_return(&device_count);   //原子变量+1,以获得原子锁,进入临界区操作。
        sdev->dev = device_create(switch_class, NULL,     
        MKDEV(0, sdev->index), NULL, sdev->name);     //创建一个名为sdev->name,设备号为MKDEV(0, sdev->index)的switch类设备。
        if (IS_ERR(sdev->dev))        //如果创建不成功就返回。
            return PTR_ERR(sdev->dev);
        ret = device_create_file(sdev->dev, &dev_attr_state);    //为设备添加 sysfs attribute:state。
        if (ret < 0)
            goto err_create_file_1;
        ret = device_create_file(sdev->dev, &dev_attr_name);    //为设备添加 sysfs attribute:name
        if (ret < 0)
            goto err_create_file_2;
        dev_set_drvdata(sdev->dev, sdev);   //给设备初始化。
        sdev->state = 0;
        return 0;
    err_create_file_2:
        device_remove_file(sdev->dev, &dev_attr_state);
    err_create_file_1:
        device_destroy(switch_class, MKDEV(0, sdev->index));
        printk(KERN_ERR "switch: Failed to register driver %s\n", sdev->name);
        return ret;
    }
    EXPORT_SYMBOL_GPL(switch_dev_register);
    

    switch_set_state(&sdev, state); 这个方法如何修改了state的值

    void switch_set_state(struct switch_dev *sdev, int state)
    {
        char name_buf[120];
        char state_buf[120];
        char *prop_buf;
        char *envp[3];
        int env_offset = 0;
        int length;
        if (sdev->state != state) {
            sdev->state = state;        //如果设备状态不等于state,则赋值state.
            prop_buf = (char *)get_zeroed_page(GFP_KERNEL);    //获得一页初始化为0的内存 prop_buf。
            if (prop_buf) {
                length = name_show(sdev->dev, NULL, prop_buf);   //输出设备名 到prop_buf 中.
                if (length > 0) {
                    if (prop_buf[length - 1] == '\n')
                    prop_buf[length - 1] = 0;
                    snprintf(name_buf, sizeof(name_buf),
                        "SWITCH_NAME=%s", prop_buf);            //打印switch name
                    envp[env_offset++] = name_buf;
                }
                length = state_show(sdev->dev, NULL, prop_buf);
                if (length > 0) {
                    if (prop_buf[length - 1] == '\n')
                        prop_buf[length - 1] = 0;
                        snprintf(state_buf, sizeof(state_buf),    //打印switch state
                            "SWITCH_STATE=%s", prop_buf);
                        envp[env_offset++] = state_buf;
                }
                envp[env_offset] = NULL;
                kobject_uevent_env(&sdev->dev->kobj, KOBJ_CHANGE, envp); //给该switch设备传输一份带envp[]的数据。
                free_page((unsigned long)prop_buf);  //释放申请的内存。
            } else {
                printk(KERN_ERR "out of memory in switch_set_state\n");
                kobject_uevent(&sdev->dev->kobj, KOBJ_CHANGE);   //notify userspace by ending an uevent 通知用户空间,uevent操作结束。
            }
        }
    }
    EXPORT_SYMBOL_GPL(switch_set_state);
    

    如果我们想要自定义name 和 state 的返回值操作方式,可以在定义switch dev 传递函数指针,否则使用默认的赋值操作

    static ssize_t state_show(struct device *dev, struct device_attribute *attr,
                                char *buf)
    {
        struct switch_dev *sdev = (struct switch_dev *)
        dev_get_drvdata(dev);
        if (sdev->print_state) {
            int ret = sdev->print_state(sdev, buf);   //打印state
            if (ret >= 0)
            return ret;
        }
        return sprintf(buf, "%d\n", sdev->state); //默认state值作为字符串返回
    }
    static ssize_t name_show(struct device *dev, struct device_attribute *attr,
                            char *buf)
    {
        struct switch_dev *sdev = (struct switch_dev *)
        dev_get_drvdata(dev);
        if (sdev->print_name) {
            int ret = sdev->print_name(sdev, buf);   //打印name
            if (ret >= 0)
                return ret;
        }
        return sprintf(buf, "%s\n", sdev->name);    //默认返回name 字符串
    }
    static DEVICE_ATTR(state, S_IRUGO | S_IWUSR, state_show, NULL);    //给用户空间提供接口
    static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, name_show, NULL);
    

    前面demo的例子就是使用默认的返回值方法

    当我们cat name 的时候返回hello, cat state的时候就返回 0 或 1,这个根据驱动具体使用情况来确认

    二、Android Uevent

    Uevent 在 Linux-3.x 上是基于 NetLink 来实现的。其实现思路是,首先在内核中调用 netlink_kernel_create() 函数创建一个socket套接字;当有事件发生的时候,则通过 kobject_uevent() 最终调用 netlink_broadcast_filtered() 向 userspace 发送数据。如果同时在 userspace ,有在监听该事件,则两相一合,kernel 的“变化”,userspace 即刻知晓。 Kernel kernel ,关于 uevent 的实现代码,大约可参考文件 kobject_uevent.c ,其简要调用如下:

    kobject_uevent(&drv->p->kobj, KOBJ_ADD);  
    kobject_uevent_env(kobj, action, NULL);  
    retval = netlink_broadcast_filtered(uevent_sock, skb,0, 1, GFP_KERNEL,kobj_bcast_filter,kobj);  
    

    其中,kobject_uevent(struct kobject *kobj, enum kobject_action action) 中的 action 对应着以下几种:

    KOBJ_ADD,  
    KOBJ_REMOVE,  
    KOBJ_CHANGE,  
    KOBJ_MOVE,  
    KOBJ_ONLINE,
    KOBJ_OFFLINE,  
    

    而 kobject_uevent() 其实就是直接调用了 kobject_uevent_env() 函数。一切的操作,将在该函数中完成,比如 kset uevent ops (struct kset_uevent_ops)的获取、字符串的填充组合、netlink message 的发送等。 其中, kset_uevent_ops 有以下几种:

    slab_uevent_ops  
    bus_uevent_ops  
    device_uevent_ops  
    gfs2_uevent_ops  
    module_uevent_ops  
    

    这些 uevent ops 在 start_kernel() 就会被注册。

    1、Android uevent架构

    Android很多事件都是通过uevent跟kernel来异步通信的。其中类UEventObserver是核心。

    UEventObserver接收kernel的uevent信息的抽象类。

    1、java层代码

    frameworks/base/core/java/android/os/UEventObserver.java

    2、JNI层代码

    frameworks/base/core/jni/android_os_UEventObserver.cpp

    3、底层代码

    hardware/libhardware_legacy/uevent/uevent.c

    读写kernel的接口socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);

    2、UEventObserver的使用

    类UEventObserver提供了三个接口给子类来调用:

    1、onUEvent(UEvent event)

    子类必须重写这个onUEvent来处理uevent。

    2、startObserving(String match)

    启动进程,要提供一个字符串参数。

    3、stopObserving()

    停止进程。

    3、Java Uevent 使用 Demo

    private UEventObserver mHelloObserver = new UEventObserver() {
        @Override
        public void onUEvent(UEventObserver.UEvent event) {
            String name = event.get("SWITCH_NAME");
            String state = event.get("SWITCH_STATE");
            // ..do someting..
        }
    }
    
    if (new File("/sys/devices/virtual/switch/hello/state").exists()) {
        mHelloObserver.startObserving("DEVPATH=/devices/virtual/switch/hello");
    }
    

    当Kernel中hello switch module中的state 值发生变化时

    Android 中的 onUEvent 回调就会被触发了

    相关文章

      网友评论

          本文标题:Android Uevent 结合 Kernel Switch

          本文链接:https://www.haomeiwen.com/subject/tzvwektx.html