綜述
項(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)
可以看到,確實(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
因此癣蟋,我們要往系統(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
注釋掉以下代碼
以上這種添加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();
}
荊軻刺秦王