Linux 驱动下创建设备节点的方式

本文介绍了在Linux下创建设备节点的三种方法:1) 使用DEVICE_ATTR在/sys/class下创建节点;2) 利用proc_create在/proc目录下创建文件节点;3) 通过sysfs_create_file创建文件节点,并详细阐述了各方法的函数参数及注意事项,包括权限设置、读写操作函数的实现以及用户空间和内核空间数据交互的处理。

  最近在学习Android下的LCD模块,在编写新需求时,要求底层提供节点给上层调用,这里我总结的三种Linux下如何创建设备节点的方法,而创建设备节点调用的函数都是Linux内核里面提供的函数接口,我们只需要调用这些函数接口去创建节点即可。

1. /sys/class/下面某个目录下创建hello这个节点,可以通过调用以下函数:
示例:static DEVICE_ATTR(hello, S_IRUGO | S_IWUSR, hello_read, hello_wirte);

第一个参数:要创建的节点名字;
第二个参数:创建节点的权限;
第三个参数:向/sys/class/xxx/hello节点读取数据的时候,调用这个函数指针参数(读函数名称);
第四个参数:向/sys/class/xxx/hello节点写入数据的时候,调用这个函数指针参数(写函数名称);

具体说明在在documentation/driver-model/Device.txt中;

#define DEVICE_ATTR(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
dev_attr_##_name!!!!!

#define __ATTR(_name,_mode,_show,_store) { \
 .attr = {.name = __stringify(_name), .mode = _mode }, \
 .show = _show,     \
 .store = _store,     \
}

device_attribute定义:
struct device_attribute {
 struct attribute attr;
 ssize_t (*show)(struct device *dev, struct device_attribute *attr,
   char *buf);
 ssize_t (*store)(struct device *dev, struct device_attribute *attr,
    const char *buf, size_t count);
};

​

2. /proc目录下创建文件节点,可调用内核中的创建函数:
示例:struct proc_dir_entry *proc_create(const char *name, umode_t mode, struct proc_dir_entry *parent, const struct file_operations *proc_fops)
name: 表示要创建的节点名字;
mode: 表示创建节点的权限;
parent: 父entry,为NULL的话,默认父entry是/proc;
proc_fops:表示对这个文件节点进行读写操作的函数的结构体;

踩过的坑经验:

(1). 在对proc_fops结构体里面的读写函数,进行实现的时候,写函数的返回值必须是写入的字节数,否者内核一直循环的去写这个值;

(2). 同时在编写驱动文件对应的文件操作函数时,在添加打印信息时,一定不能把用户空间的字符串用在内核空间打印,否则会导致Linux系统在加载这个函数时,系统崩溃;

(3). 在这里额文件操作函数要注意传参,buf数据都是用户空间的,要加__user去修饰变量,这个和编写一个简单的字符设备驱动对应的读写函数传的参是一样的,要使用copy_to_user()和copy_from_user()宏进行内核空间和用户空间数据的交互;

static inline struct proc_dir_entry *proc_create(
    const char *name, umode_t mode, struct proc_dir_entry *parent,
    const struct file_operations *proc_fops)
{
    return proc_create_data(name, mode, parent, proc_fops, NULL);
}
​
struct file_operations {
	struct module *owner;
	loff_t (*llseek) (struct file *, loff_t, int);
	ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
	ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
	int (*open) (struct inode *, struct file *);
	int (*release) (struct inode *, struct file *);
        ...
        ...
};

​

3. 调用内核函数sysfs_create_file(struct kobject *hellowold_kobj, struct kobj_attribute hello_value_attribute); 去创建文件节点,节点对应的读写函数,分别为show(),store()函数;

而show()函数和store()函数的注册实在 struct kobj_attribute hello_value_attribute结构体中;

struct kobj_attribute {
	struct attribute attr;
	ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr,
			char *buf);
	ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr,
			 const char *buf, size_t count);
};

大家可以看到show()函数和store()函数中的参数buf前面没有__user的修饰符,证明buf存放的数据是用户空间的数据,所以我们这个时候不能调用copy_to_user()和copy_from_user()宏了,可以使用snprintf()函数和sscanf()函数,来进行操作数据,把要读取的数据和写入的数据传递给新的变量,然后去操作这个新的内核变量,而buf这个变量数据内核会对它进行操作改变,我们不需要关心这个,因为我们只是在调用内核函数接口,剩下的内核会帮助我们去处理。如果你想弄懂内核的机制,你可以追内核源代码。

在show函数中,要调用snprintf()函数,把内核空间的数据放到用户空间去;
在store函数中,要调用sscanf()函数,把用户空间的数据放到内核空间去;
 sprintf和sscanf类似与对copy_from_user和copy_to_user函数的封装。

 

最后,如果大家对每一种创建节点的方式要了解的更清楚一下,可以猛戳一下链接:

1、https://blog.csdn.net/njuitjf/article/details/16849333

2、https://www.cnblogs.com/ck1020/p/7475729.html

3、https://blog.csdn.net/yj4231/article/details/7799245

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值