节点的作用
安卓是基于linux的虚拟机。而linux系统一切都是文件,节点类似创建一个文件。通常创建的文件节点一般有以下作用。
1、用于上层读取设备信息。例如屏幕名称,摄像头名称,TP固件版本等信息。
2、用于上层向底层写入信息。例如TP模拟贴脸灭屏功能的开启关闭。
节点创建的方法
节点创建的方法有很多,BSP工程师常见的一般有两种。
方法一(device_create_file)
代码原型如下:
int device_create_file(struct device *dev, struct device_attribute *attr);
参数
dev:指向设备结构体的指针。
attr:指向设备属性结构体的指针。
以上参数中dev创建的位置决定了attr节点的位置。
例如创建了一个platform_device的dev,通过这个dev创建的节点,节点路径为:/sys/devices/platform/(dev的名称)/(attr节点的名称)
/* add for miki-tpd*/
示例代码为MTK平台创建TP固件版本号的示例,路径为:/sys/devices/platform/miki-tpd/tp_firmware_version
/*add node tp_version*/
static ssize_t ovt_tcm_tp_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
char *ptr = buf;
printk("ovt_tp_version_show\n");
ptr += sprintf(ptr," Firmware:%d \n",g_tcm_hcd->packrat_number);
printk("ovt_tp_version_show:%s\n",ptr);
return (ptr-buf);
}
static DEVICE_ATTR(tp_firmware_version, 0664, ovt_tcm_tp_version_show, NULL);
static int mikinode_probe(struct platform_device *dev)
{
int ret = 0;
printk("%s: enty\n",__func__);
ret = device_create_file(&(dev->dev),&dev_attr_tp_firmware_version);
if(ret)
{
printk("%s: device_create_file tp_firmware_version failed\n",__func__);
}
ret = device_create_file(&(dev->dev),&dev_attr_touch_ic);
if(ret)
{
printk("%s: device_create_file touch_ic failed\n",__func__);
}
ret = device_create_file(&(dev->dev),&dev_attr_tp_vendor);
if(ret)
{
printk("%s: device_create_file tp_vendor failed\n",__func__);
}
return ret;
}
static struct platform_driver mikinode_driver = {
.driver = {
.name = "miki-tpd",
.owner = THIS_MODULE,
},
.probe = mikinode_probe,
};
#ifdef CONFIG_OF
static struct platform_device mikinode_device = {
.name = "miki-tpd",
.id = -1,
};
#endif
static int mikinode_init(void)
{
int err;
err = platform_device_register(&mikinode_device);
if(err)
{
printk("mikinode_init: failed to register device\n");
return -ENODEV;
}
err = platform_driver_register(&mikinode_driver);
if(err)
{
printk("mikinode_init: failed to register driver\n");
return -ENODEV;
}
return err;
}
static void mikinode_exit(void)
{
printk("mikinode_exit: remove device\n");
platform_device_unregister(&mikinode_device);
}
/* add miki-tpd end*/
以上代码为引用代码更改的
引用路径文末提供
以下代码为新建一个虚拟的节点,故节点位置为:/sys/devices/virtual/(class名称)/(device名称)/(节点名称)
//add
static ssize_t cts_tp_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
extern struct chipone_ts_data *cts_data;
char *ptr = buf;
cts_info("cts_tp_version_show");
ptr += sprintf(ptr,"IC type : icnl9916\n");
ptr += sprintf(ptr,"Firmware : 0x%x\n",cts_data->cts_dev.fwdata.version);
cts_info("cts_tp_version_show:%s",ptr);
return (ptr-buf);
}
static DEVICE_ATTR(tp_version, S_IRUSR, cts_tp_version_show, NULL);
static struct class *touchscreen_class;
static struct device *touchscreen_class_dev;
//end
在调用的代码内添加
//add
touchscreen_class = class_create(THIS_MODULE, "touchscreen");
if (IS_ERR(touchscreen_class)) {
cts_info("create touchscreen class failed");
return -EPERM;
}
touchscreen_class_dev = device_create(touchscreen_class, NULL,0,NULL, "device");
if (IS_ERR(touchscreen_class_dev)) {
cts_info("create touchscreen device failed");
return -EPERM;
}
ret = device_create_file(touchscreen_class_dev, &dev_attr_tp_version);
if(ret)
{
cts_info("create /sys/devices/virtual/touchscreen/device/tp_version failed\n");
}
//end
方法二(sysfs_create_group)
代码原型如下:
int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp);
参数
kobj:内核对象的指针。
grp:属性组的指针,包含一组属性定义。
以上函数中kobj参数决定了节点创建的文件路径,该参数需要使用函数kobject_create_and_add创建一个对象。
kobject_create_and_add的代码原型为:
struct kobject *kobject_create_and_add(const char *name, struct kobject *parent);
参数
name:新创建的内核对象的名称。
parent:父内核对象的指针,可以是NULL,表示新对象是顶级对象。
返回值
成功时返回新创建的内核对象的指针。
失败时返回错误指针(如 ERR_PTR)。
kobject_create_and_add函数中name字符串为创建的对象的名称对应节点/sys/(parent的路径)/(name字符串)
sysfs_create_group函数的grp参数内是一个或多个节点
代码示例,以下代码创建的节点位置为:/sys/lcm/lcm_name
/* add lcm_name*/
static struct kobject *k_obj = NULL;
static ssize_t show_lcm_name(struct kobject *kobj, struct kobj_attribute *attr, char * buf)
{
return sprintf(buf, "TD4160b-hy\n");
}
static struct kobj_attribute lcm_name_attribute = __ATTR(lcm_name, 0664, show_lcm_name, NULL);
static struct attribute *sysfs_lcm_attribute[] = {&lcm_name_attribute.attr, NULL};
static struct attribute_group sysfs_lcm_attr_group = {.attrs = sysfs_lcm_attribute,};
/* add lcm_name end*/
在lcm_probe函数内添加
/* add lcm_name node*/
if ((k_obj = kobject_create_and_add("lcm", NULL)) == NULL )
{
pr_err("lcm sys node create error\n");
}
if(sysfs_create_group(k_obj, &sysfs_lcm_attr_group) ) {
pr_err("sysfs_create_group failed\n");
kobject_put(k_obj);
}
/* add lcm_name node end*/
在remove内添加
/* add lcm_name node*/
if (k_obj) {
sysfs_remove_group(k_obj, &sysfs_lcm_attr_group);
kobject_put(k_obj);
}
/* add lcm_name end*/
方案个人总结
如果创建节点位置任意,使用device_create_file创建,该函数创建需要device参数,常可以在驱动创建完成后直接创建节点,比较方便。当驱动remove时使用函数device_remove_file
如果指定了节点路径,并且节点较多,使用sysfs_create_group,当驱动remove时使用函数sysfs_remove_group
sysfs_create_group使用后
在remove内添加
/* add lcm_name node*/
if (k_obj) {
sysfs_remove_group(k_obj, &sysfs_lcm_attr_group);
kobject_put(k_obj);
}
/* add lcm_name end*/
文献引用
【1】MTK的TP信息节点创建:https://www.jianshu.com/p/11af0207231c(MTK平台-添加TP版本号节点(/sys/devices/platform/路径下添加节点))