MTK平臺(tái)-添加閃光燈測試節(jié)點(diǎn)

綜述

項(xiàng)目需求:客戶有一個(gè)老化apk辛燥,自動(dòng)測試閃光燈断凶。
新增以下節(jié)點(diǎn)桨仿,控制閃光燈亮滅喜庞。
/sys/devices/virtual/torch/torch/torch_level
寫入1 么鹤,后手電筒亮
寫入0 终娃,后手電筒滅
/sys/devices/virtual/sub_torch/sub_torch/sub_torch_level
寫入1 ,前手電筒亮
寫入0 蒸甜,前手電筒滅

實(shí)現(xiàn)思路
1.創(chuàng)建/sys/devices/virtual/torch/torch/torch_level節(jié)點(diǎn)
2.在寫入1的時(shí)候棠耕,打開閃光燈,寫入0的時(shí)候柠新,關(guān)閉閃光燈
················································································
怎么在/sys/devices/virtual/路徑下創(chuàng)建節(jié)點(diǎn)呢窍荧?很蒙逼
怎么在底層調(diào)用打開或者關(guān)閉閃光燈呢?很懵逼

學(xué)習(xí)本文恨憎,講解決以上問題蕊退。

1.創(chuàng)建節(jié)點(diǎn)

這里以/sys/devices/virtual/torch/torch/torch_level節(jié)點(diǎn)創(chuàng)建為例子,

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

#define TORCH_DEVNAME "torch"http://節(jié)點(diǎn)名稱
static dev_t torch_devno;//設(shè)備號(hào)
static struct class *torch_class;//class類
static struct device *torch_device;//設(shè)備
static int main_torch_level = 0;

//show函數(shù)
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函數(shù)
static ssize_t torch_store(struct device *dev,
               struct device_attribute *attr, const char *buf, size_t count)
{
    //添加閃光燈控制邏輯
    return count;
}
//定義設(shè)備屬性節(jié)點(diǎn)torch_level,和show函數(shù)咕痛,store函數(shù)
static DEVICE_ATTR(torch_level, S_IRUGO|S_IWUSR, torch_show, torch_store);

//初始化函數(shù)
static void torch_create(void)
{  
    int ret = 0;
    logI("zcf [%s] is init\n",__func__);
    //申請?jiān)O(shè)備號(hào)--下面需要用的這個(gè)設(shè)備號(hào)
    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;   這里應(yīng)該跳到unregister函數(shù)痢甘,懶得寫
    } else {//申請失敗
        logI("zcf [torch_init] major: %d, minor: %d ~", MAJOR(torch_devno),
             MINOR(torch_devno));
    } 
    //在sys/class創(chuàng)建torch_class 類
    torch_class = class_create(THIS_MODULE, TORCH_DEVNAME);
    if (IS_ERR(torch_class)) {//創(chuàng)建失敗
        logI("zcf  Unable to create class:torch_class\n");
    }
    //在sys/class/torch_class/下創(chuàng)建設(shè)備節(jié)點(diǎn)
    torch_device =device_create(torch_class, NULL, torch_devno, 
                                                  NULL, TORCH_DEVNAME);
    if(NULL == torch_device)//創(chuàng)建失敗
        logI("zcf [torch_init] device_create torch_device fail \n");
    //在sys/class/torch_class/torch_class下創(chuàng)建屬性文件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();//創(chuàng)建節(jié)點(diǎn)
}

需要注意的地方
1.創(chuàng)建節(jié)點(diǎn)茉贡,最好在probe函數(shù)中創(chuàng)建Hぁ!腔丧!
2關(guān)于device_create_file函數(shù)
device_create_file(torch_device,&dev_attr_torch_level);
這個(gè)函數(shù)放椰,前面的參數(shù)是device ,后面的參數(shù)加你要?jiǎng)?chuàng)建的節(jié)點(diǎn)名稱dev_attr_xxxx愉粤。
device_create_file(torch_devicedev, &dev_attr_xxx);
dev_attr_xxx就是在xxx(要?jiǎng)?chuàng)建的節(jié)點(diǎn))前加上dev_attr_

DEVICE_ATTR,device_create_file的使用

到此砾医,我們的節(jié)點(diǎn)就創(chuàng)建出來了,有人可能會(huì)有疑問衣厘,上面代碼中創(chuàng)建的節(jié)點(diǎn)是
sys/class/torch_class/torch_class/torch_level
但要求的節(jié)點(diǎn)路徑是
/sys/devices/virtual/sub_torch/sub_torch/sub_torch_level
不對(duì)勁叭缪痢!
實(shí)際上影暴,我們創(chuàng)建sys/class/torch_class/torch_class/torch_level節(jié)點(diǎn)后错邦,系統(tǒng)會(huì)默認(rèn)也在/sys/devices/virtual/torch_class/torch_class/torch_level下創(chuàng)建一樣的節(jié)點(diǎn),使用adb命令可以查看型宙!
(為啥會(huì)自動(dòng)創(chuàng)建這個(gè)節(jié)點(diǎn)呢撬呢,這就需要跟蹤源碼啦)

adb命令查看節(jié)點(diǎn)

adb

可以看到,確實(shí)系統(tǒng)默認(rèn)創(chuàng)建了/sys/devices/virtual/torch_class/torch_class/torch_level節(jié)點(diǎn)妆兑。

2.打開魂拦、關(guān)閉 閃光燈

當(dāng)我們獲取torch_level節(jié)點(diǎn)的值(例如:使用adb命令 cat torch_level)時(shí),就會(huì)調(diào)用show函數(shù)

//show函數(shù)
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);
}

當(dāng)我們往torch_level節(jié)點(diǎn)寫入值(例如:使用adb命令 echo 1 > torch_level)時(shí)搁嗓,就會(huì)調(diào)用store函數(shù)

//store函數(shù)
static ssize_t torch_store(struct device *dev,
              struct device_attribute *attr, const char *buf, size_t count)
{
   //添加閃光燈控制邏輯
   return count;
}

因此芯勘,我們需要在store函數(shù)中添加閃光燈的控制邏輯,那么腺逛,如何打開或者關(guān)閉閃光燈呢借尿?
kernel-3.18/drivers/misc/mediatek/flashlight/src/{平臺(tái)}/{項(xiàng)目}/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)//關(guān)閉閃關(guān)燈
{
    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);
}

從源碼中,可以看到屉来,調(diào)用了FL_Enable和FL_Disable去打開、關(guān)閉閃光燈狈癞,原理就是通過拉高相應(yīng)的GPIO腳茄靠,使能閃光燈,拉低GPIO腳蝶桶,關(guān)閉閃光燈慨绳。
需要注意的是,每次調(diào)用之前,我們需要先調(diào)用GPIO初始化函數(shù)脐雪,找到相應(yīng)的GPIO引腳

extern int FL_Enable(void);//聲明FL_Enable是外部函數(shù)
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);//把字符串轉(zhuǎn)為數(shù)字
    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();//關(guān)閉閃光燈
    else//無效參數(shù)
        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函數(shù)中,就添加好了控制閃光燈的邏輯了战秋。當(dāng)然璧亚,僅僅這樣,客戶提供的第三方測試apk是無法訪問節(jié)點(diǎn)的脂信,會(huì)有selinux錯(cuò)誤

 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錯(cuò)誤

因此癣蟋,我們要往系統(tǒng)加入相應(yīng)的權(quán)限。

3.添加節(jié)點(diǎn)訪問的權(quán)限

1.device/mediatek/mt6580/init.mt6580.rc
使所以用戶對(duì)節(jié)點(diǎn)torch_level狰闪、sub_torch_level均有讀寫權(quán)限


    # 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權(quán)限的方式會(huì)導(dǎo)致GMS測試失敗疯搅,因?yàn)楦膭?dòng)了谷歌官方的邏輯-system/sepolicy/private/app.te。因此埋泵,換以下方式去改動(dòng):

1.device/mediatek/mt6580/init.mt6580.rc
使所以用戶對(duì)節(jié)點(diǎn)torch_level幔欧、sub_torch_level均有讀寫權(quán)限

    # 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對(duì)應(yīng)的label
注意對(duì)應(yīng)的節(jié)點(diǎn)是實(shí)際節(jié)點(diǎn),而不是鏈接.以及整個(gè)目錄路徑中也絕不能包含鏈接(無數(shù)同仁有犯這個(gè)錯(cuò)誤丽声,特意提醒)
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.申請相關(guān)的權(quán)限
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();
}

荊軻刺秦王

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末礁蔗,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子恒序,更是在濱河造成了極大的恐慌瘦麸,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件歧胁,死亡現(xiàn)場離奇詭異滋饲,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)喊巍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門屠缭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人崭参,你說我怎么就攤上這事呵曹。” “怎么了何暮?”我有些...
    開封第一講書人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵奄喂,是天一觀的道長。 經(jīng)常有香客問我海洼,道長跨新,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任坏逢,我火速辦了婚禮域帐,結(jié)果婚禮上赘被,老公的妹妹穿的比我還像新娘。我一直安慰自己肖揣,他們只是感情好民假,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著龙优,像睡著了一般羊异。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上陋率,一...
    開封第一講書人閱讀 51,521評(píng)論 1 304
  • 那天球化,我揣著相機(jī)與錄音,去河邊找鬼瓦糟。 笑死筒愚,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的菩浙。 我是一名探鬼主播巢掺,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼劲蜻!你這毒婦竟也來了陆淀?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤先嬉,失蹤者是張志新(化名)和其女友劉穎轧苫,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體疫蔓,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡含懊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了衅胀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片岔乔。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖滚躯,靈堂內(nèi)的尸體忽然破棺而出雏门,到底是詐尸還是另有隱情,我是刑警寧澤掸掏,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布茁影,位于F島的核電站,受9級(jí)特大地震影響丧凤,放射性物質(zhì)發(fā)生泄漏呼胚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一息裸、第九天 我趴在偏房一處隱蔽的房頂上張望蝇更。 院中可真熱鬧,春花似錦呼盆、人聲如沸年扩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽厨幻。三九已至,卻和暖如春腿时,著一層夾襖步出監(jiān)牢的瞬間况脆,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來泰國打工批糟, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留格了,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓徽鼎,卻偏偏與公主長得像盛末,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子否淤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容