美文网首页
MTK平台-添加闪光灯测试节点

MTK平台-添加闪光灯测试节点

作者: c枫_撸码的日子 | 来源:发表于2018-10-24 11:04 被阅读0次

    综述

    项目需求:客户有一个老化apk,自动测试闪光灯。
    新增以下节点,控制闪光灯亮灭。
    /sys/devices/virtual/torch/torch/torch_level
    写入1 ,后手电筒亮
    写入0 ,后手电筒灭
    /sys/devices/virtual/sub_torch/sub_torch/sub_torch_level
    写入1 ,前手电筒亮
    写入0 ,前手电筒灭

    实现思路
    1.创建/sys/devices/virtual/torch/torch/torch_level节点
    2.在写入1的时候,打开闪光灯,写入0的时候,关闭闪光灯
    ················································································
    怎么在/sys/devices/virtual/路径下创建节点呢?很蒙逼
    怎么在底层调用打开或者关闭闪光灯呢?很懵逼

    学习本文,讲解决以上问题。

    1.创建节点

    这里以/sys/devices/virtual/torch/torch/torch_level节点创建为例子,

    kernel-3.18/drivers/misc/mediatek/flashlight/src/mt6580/kd_flashlightlist.c

    #define TORCH_DEVNAME "torch"//节点名称
    static dev_t torch_devno;//设备号
    static struct class *torch_class;//class类
    static struct device *torch_device;//设备
    static int main_torch_level = 0;
    
    //show函数
    static ssize_t torch_show(struct device *dev,
                                    struct device_attribute *attr, char *buf)
    {
        logI("zcf torch_show is call,main_torch_level=%d\n",main_torch_level);
        return snprintf(buf, PAGE_SIZE, "%d\n", main_torch_level);
    }
    //store函数
    static ssize_t torch_store(struct device *dev,
                   struct device_attribute *attr, const char *buf, size_t count)
    {
        //添加闪光灯控制逻辑
        return count;
    }
    //定义设备属性节点torch_level,和show函数,store函数
    static DEVICE_ATTR(torch_level, S_IRUGO|S_IWUSR, torch_show, torch_store);
    
    //初始化函数
    static void torch_create(void)
    {  
        int ret = 0;
        logI("zcf [%s] is init\n",__func__);
        //申请设备号--下面需要用的这个设备号
        ret = alloc_chrdev_region(&torch_devno, 0, 1, TORCH_DEVNAME);
        if (ret) {//申请成功
            logI("zcf [torch_init] alloc_chrdev_region fail: %d ~", ret);
            //goto torch_chrdev_error;   这里应该跳到unregister函数,懒得写
        } else {//申请失败
            logI("zcf [torch_init] major: %d, minor: %d ~", MAJOR(torch_devno),
                 MINOR(torch_devno));
        } 
        //在sys/class创建torch_class 类
        torch_class = class_create(THIS_MODULE, TORCH_DEVNAME);
        if (IS_ERR(torch_class)) {//创建失败
            logI("zcf  Unable to create class:torch_class\n");
        }
        //在sys/class/torch_class/下创建设备节点
        torch_device =device_create(torch_class, NULL, torch_devno, 
                                                      NULL, TORCH_DEVNAME);
        if(NULL == torch_device)//创建失败
            logI("zcf [torch_init] device_create torch_device fail \n");
        //在sys/class/torch_class/torch_class下创建属性文件torch_level
        ret = device_create_file(torch_device,&dev_attr_torch_level);
        if(ret < 0)
            logI("zcf Failed to create attribute torch_levle\n");
    }
    
    static int flashlight_probe(struct platform_device *dev)
    {
        //省略代码
        torch_create();//创建节点
    }
    

    需要注意的地方
    1.创建节点,最好在probe函数中创建!!!
    2关于device_create_file函数
    device_create_file(torch_device,&dev_attr_torch_level);
    这个函数,前面的参数是device ,后面的参数加你要创建的节点名称dev_attr_xxxx。
    device_create_file(torch_devicedev, &dev_attr_xxx);
    dev_attr_xxx就是在xxx(要创建的节点)前加上dev_attr_

    DEVICE_ATTR,device_create_file的使用

    到此,我们的节点就创建出来了,有人可能会有疑问,上面代码中创建的节点是
    sys/class/torch_class/torch_class/torch_level
    但要求的节点路径是
    /sys/devices/virtual/sub_torch/sub_torch/sub_torch_level
    不对劲啊!
    实际上,我们创建sys/class/torch_class/torch_class/torch_level节点后,系统会默认也在/sys/devices/virtual/torch_class/torch_class/torch_level下创建一样的节点,使用adb命令可以查看!
    (为啥会自动创建这个节点呢,这就需要跟踪源码啦)

    adb命令查看节点

    adb

    可以看到,确实系统默认创建了/sys/devices/virtual/torch_class/torch_class/torch_level节点。

    2.打开、关闭 闪光灯

    当我们获取torch_level节点的值(例如:使用adb命令 cat torch_level)时,就会调用show函数

    //show函数
    static ssize_t torch_show(struct device *dev,
                                    struct device_attribute *attr, char *buf)
    {
        logI("zcf torch_show is call,main_torch_level=%d\n",main_torch_level);
        return snprintf(buf, PAGE_SIZE, "%d\n", main_torch_level);
    }
    

    当我们往torch_level节点写入值(例如:使用adb命令 echo 1 > torch_level)时,就会调用store函数

    //store函数
    static ssize_t torch_store(struct device *dev,
                  struct device_attribute *attr, const char *buf, size_t count)
    {
       //添加闪光灯控制逻辑
       return count;
    }
    

    因此,我们需要在store函数中添加闪光灯的控制逻辑,那么,如何打开或者关闭闪光灯呢?
    kernel-3.18/drivers/misc/mediatek/flashlight/src/{平台}/{项目}/constant_flashlight/leds_strobe.c

    int FL_Enable(void)//打开闪光灯
    {
        if (g_duty>=1)
        {   
            mflash_set_gpio_output(GPIO_ENT,GPIO_OUT_ONE);
            mflash_set_gpio_output(GPIO_ENF,GPIO_OUT_ONE);
            FL_ALOGD(" FL_Enable enable in flash mode,duty=%d.", g_duty);
        }        
        else
        {   
            mflash_set_gpio_output(GPIO_ENT,GPIO_OUT_ONE);
            mflash_set_gpio_output(GPIO_ENF,GPIO_OUT_ZERO);
            FL_ALOGD(" FL_Enable enable in torch mode,duty=%d.", g_duty);
        }   
    
        return 0;
    }
    
    int FL_Disable(void)//关闭闪关灯
    {
        FL_ALOGF();
        mflash_set_gpio_output(GPIO_ENT,GPIO_OUT_ZERO);
        mflash_set_gpio_output(GPIO_ENF,GPIO_OUT_ZERO);
        return 0;
    }
    //初始化GPIO引脚
    void main_camera_flash_gpio_init(void) 
    {
        static struct device_node *main_flash_node;
        FL_ALOGF();
        main_flash_node = of_find_compatible_node(NULL, NULL, 
                                                    "mediatek,mainflashlight");
        GPIO_ENT = of_get_named_gpio(main_flash_node, 
                                                    "flashlight_en_gpio", 0);
        GPIO_ENF = of_get_named_gpio(main_flash_node, 
                                                    "flashlight_mode_gpio", 0);
    }
    
    

    从源码中,可以看到,调用了FL_Enable和FL_Disable去打开、关闭闪光灯,原理就是通过拉高相应的GPIO脚,使能闪光灯,拉低GPIO脚,关闭闪光灯。
    需要注意的是,每次调用之前,我们需要先调用GPIO初始化函数,找到相应的GPIO引脚

    extern int FL_Enable(void);//声明FL_Enable是外部函数
    extern int FL_Disable(void);
    extern void main_camera_flash_gpio_init(void);
    
    static ssize_t torch_store(struct device *dev,
                     struct device_attribute *attr, const char *buf, size_t count)
    {
        int temp;
        temp = simple_strtol(buf, NULL, 10);//把字符串转为数字
        main_torch_level = temp;
        main_camera_flash_gpio_init();//初始化gpio引脚
        if(main_torch_level == 1)//如果写1
        {   
            FL_Enable();//打开闪光灯
        }
        else if(main_torch_level == 0)//如果写0
            FL_Disable();//关闭闪光灯
        else//无效参数
            logI("zcf invalid parameter,the torch_level must be 1 or 0\n");
    
        logI("zcf  main_torch_level=%d , count =%d\n",main_torch_level,count);
        return count;
    }
    

    这样,我们在store函数中,就添加好了控制闪光灯的逻辑了。当然,仅仅这样,客户提供的第三方测试apk是无法访问节点的,会有selinux错误

     avc: denied { write } for name="torch_level" dev="sysfs" ino=9205 
    scontext=u:r:system_app:s0 tcontext=u:object_r:sysfs:s0 
    tclass=file permissive=0
    
    avc错误

    因此,我们要往系统加入相应的权限。

    3.添加节点访问的权限

    1.device/mediatek/mt6580/init.mt6580.rc
    使所以用户对节点torch_level、sub_torch_level均有读写权限

    
        # start-add by zcf in 2018.10.23 for torch_test
        chmod 0666 /sys/devices/virtual/torch/torch/torch_level
        chown root system /sys/devices/virtual/torch/torch/torch_level
    
        chmod 0666 /sys/devices/virtual/sub_torch/sub_torch/sub_torch_level
        chown root system /sys/devices/virtual/sub_torch/sub_torch/sub_torch_level
        # end-add by zcf in 2018.10.23 for torch_test
    

    2.device/mediatek/sepolicy/basic/non_plat/system_app.te

    # add by zcf in 2018.10.23 for torch test
    allow system_app sysfs:file { write read };
    

    3.system/sepolicy/private/app.te
    注释掉以下代码

    app.te

    以上这种添加SELinux权限的方式会导致GMS测试失败,因为改动了谷歌官方的逻辑-system/sepolicy/private/app.te。因此,换以下方式去改动:

    1.device/mediatek/mt6580/init.mt6580.rc
    使所以用户对节点torch_level、sub_torch_level均有读写权限

        # start-add by zcf in 2018.10.23 for torch_test
        chmod 0666 /sys/devices/virtual/torch/torch/torch_level
        chown root system /sys/devices/virtual/torch/torch/torch_level
    
        chmod 0666 /sys/devices/virtual/sub_torch/sub_torch/sub_torch_level
        chown root system /sys/devices/virtual/sub_torch/sub_torch/sub_torch_level
        # end-add by zcf in 2018.10.23 for torch_test
    

    2.自定义类型: 定义torch SELinux type
    device/mediatek/sepolicy/bsp/non_plat/file.te

    #add by zcf in 2018.11.1 for flashilight test
    type system_app_torch_file,fs_type,sysfs_type;
    type system_app_sub_torch_file,fs_type,sysfs_type;
    

    3.file_contexts 绑定 torch对应的label
    注意对应的节点是实际节点,而不是链接.以及整个目录路径中也绝不能包含链接(无数同仁有犯这个错误,特意提醒)
    device/mediatek/sepolicy/bsp/non_plat/file_contexts

    #add by zcf in 2018.11.01 for torch test
    /sys/devices/virtual/torch/torch/torch_level u:object_r:system_app_torch_file:s0
    /sys/devices/virtual/sub_torch/sub_torch/sub_torch_level u:object_r:system_app_sub_torch_file:s0
    

    4.申请相关的权限
    device/mediatek/sepolicy/basic/non_plat/system_app.te

    # add by zcf in 2018.10.23 for torch test
    allow system_app system_app_torch_file:file rw_file_perms;
    allow system_app system_app_sub_torch_file:file rw_file_perms;
    

    4.完整源码

    kernel-3.18/drivers/misc/mediatek/flashlight/src/mt6580/kd_flashlightlist.c

    //start-add by zcf in 2018.10.23 for torch test
    #define TORCH_DEVNAME "torch"
    #define SUB_TORCH_DEVNAME "sub_torch"
    static dev_t torch_devno,sub_torch_devno;
    static struct class *torch_class,*sub_torch_class;
    static struct device *torch_device,*sub_torch_device;
    
    extern void main_camera_flash_gpio_init(void);
    extern void sub_camera_flash_gpio_init(void);
    
    extern int FL_Enable(void);
    extern int FL_Disable(void);
    extern int FL_Sub_Enable(void);
    extern int FL_Sub_Disable(void);
    
    static int main_torch_level = 0;
    static int sub_torch_level = 0;
    static ssize_t torch_show(struct device *dev,struct device_attribute *attr, char *buf)
    {
        logI("zcf torch_show is call,main_torch_level=%d\n",main_torch_level);
        return snprintf(buf, PAGE_SIZE, "%d\n", main_torch_level);
    }
    
    static ssize_t torch_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t count)
    {
        int temp;
        temp = simple_strtol(buf, NULL, 10);
        main_torch_level = temp;
        main_camera_flash_gpio_init();
        if(main_torch_level == 1)
        {   
            FL_Enable();
        }
        else if(main_torch_level == 0)
            FL_Disable();
        else
            logI("zcf invalid parameter,the torch_level must be 1 or 0\n");
    
        logI("zcf torch_store is call,main_torch_level=%d , count =%d\n",main_torch_level,count);
        return count;
    }
    static DEVICE_ATTR(torch_level, S_IRUGO|S_IWUSR, torch_show, torch_store);
    
    
    static ssize_t sub_torch_show(struct device *dev,struct device_attribute *attr, char *buf)
    {
        logI("zcf torch_show is call,main_torch_level=%d\n",main_torch_level);
        return snprintf(buf, PAGE_SIZE, "%d\n", main_torch_level);
    }
    
    static ssize_t sub_torch_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t count)
    {
        int temp;
        temp = simple_strtol(buf, NULL, 10);
        sub_torch_level = temp;
        sub_camera_flash_gpio_init();
        if(sub_torch_level == 1)
        {   
            FL_Sub_Enable();
        }
        else if(sub_torch_level == 0)
            FL_Sub_Disable();
        else
            logI("zcf invalid parameter,the sub_torch_level must be 1 or 0\n");
    
        logI("zcf torch_store is call,sub_torch_level=%d , count =%d\n",sub_torch_level,count);
        return count;
    }
    
    static DEVICE_ATTR(sub_torch_level, S_IRUGO|S_IWUSR, sub_torch_show, sub_torch_store);
    
    static void torch_create(void)
    {   
        int ret = 0;
        logI("zcf [%s] is init\n",__func__);
        ret = alloc_chrdev_region(&torch_devno, 0, 1, TORCH_DEVNAME);
        if (ret) {
            logI("zcf [torch_init] alloc_chrdev_region fail: %d ~", ret);
            //goto torch_init_error;
        } else {
            logI("zcf [torch_init] major: %d, minor: %d ~", MAJOR(torch_devno),
                 MINOR(torch_devno));
        } 
        ret = alloc_chrdev_region(&sub_torch_devno, 0, 1, SUB_TORCH_DEVNAME);
    
        if (ret) {
            logI("zcf [torch_init] alloc_chrdev_region fail: %d ~", ret);
            //goto torch_init_error;
            logI("zcf [torch_init] major: %d, minor: %d ~", MAJOR(sub_torch_devno),
                 MINOR(sub_torch_devno));
        } 
        torch_class = class_create(THIS_MODULE, TORCH_DEVNAME);
        if (IS_ERR(torch_class)) {
            logI("zcf  Unable to create class:torch_class\n");
        }
    
        torch_device =device_create(torch_class, NULL, torch_devno, NULL, TORCH_DEVNAME);
        if(NULL == torch_device)
            logI("zcf [torch_init] device_create torch_device fail \n");
        ret = device_create_file(torch_device,&dev_attr_torch_level);
        if(ret < 0)
            logI("zcf Failed to create attribute torch_levle\n");
        
        sub_torch_class = class_create(THIS_MODULE, SUB_TORCH_DEVNAME);
        if (IS_ERR(sub_torch_class)) {
            logI("zcf  Unable to create class:sub_torch_class\n");
        }
    
        sub_torch_device =device_create(sub_torch_class, NULL, sub_torch_devno, NULL, SUB_TORCH_DEVNAME);
        if(NULL == sub_torch_device)
            logI("zcf [torch_init] device_create torch_device fail \n");
        ret = device_create_file(sub_torch_device,&dev_attr_sub_torch_level);
        if(ret < 0)
            logI("zcf Failed to create attribute sub_torch_levle\n");
    }
    //end-add by zcf in 2018.10.23 for torch test.
    
    static int flashlight_probe(struct platform_device *dev)
    {
        int ret = 0, err = 0;
        logI("[flashlight_probe] start ~")
        //省略部分代码
        torch_create();
    }
    

    荆轲刺秦王

    相关文章

      网友评论

          本文标题:MTK平台-添加闪光灯测试节点

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