欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

Linux驱动代码中debugfs使用详解

最编程 2024-08-13 21:24:38
...

linux系统对系统内驱动提供一个调试功能——debugfs。

debugfs是一种通过对用户空间文件进行读写操作来实现驱动和用户空间交互的调试方式。
驱动需要在系统debugfs根目录下创建一个文件,再实现对该文件的读写操作函数。这样在用户空间读写文件时,会调用驱动中的操作函数,就可进行相关的驱动内部操作了,一般用于修改驱动某个变量值或是内部消息的传递。

这个debugfs根目录地址是系统分配的,默认情况下,debugfs会被挂载在目录/sys/kernel/debug之下。cfg80211_init里创建wiphy,wiphy_register(wiphy)调用完了之后,系统会分配一个文件索引节点struct dentry *dentry=wiphy->debugfsdir。在dentry指向的目录里驱动可以创建新的目录或是直接创建文件。

我在驱动里加入以下代码:

struct my_handle *private_hd;//my_handle是驱动自定义结构体,把这个结构体指针在创建文件时就传进去,后期读写文件时会用到这个结构体指针。
struct dentry *sys_dir=wiphy->debugfsdir;//wiphy_register之后,系统分配的根目录会存在debugfsdir成员里
struct dentry *root_drv;
if (!(root_drv= debugfs_create_dir("my_test", sys_dir)))//在系统目录下建一个名为my_test的目录
        return -ERR;
if(!debugfs_create_file("number", S_IWUSR | S_IRUSR, root_drv, (void *)private_hd, &number_debugfs_ops) ) //private_hd是驱动的私有数据结构,创建文件number
    return -ERR; 

这段代码所做的操作是,在系统debug目录下,创建了mytest目录,在mytest里创建文件number,权限可读可写(S_IWUSR 对应写权限,S_IRUSR对应读权限),将操作函数绑定为number_debugfs_ops。

在我Ubuntu里,系统分配的debug目录是/sys/kernel/debug/ieee80211/phy3/,访问这个目录需要root权限,phy后面跟的数字每次insmod驱动都会分配到不同的数字。

debugfs_create_dir可以创建目录:struct dentry *debugfs_create_dir(const char *name, struct dentry *parent)//传入参数列表为(“子目录名字”, struct dentry * 结构变量指向的上一级目录)。

debugfs_create_dir函数里对目录权限的设置:inode->i_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO;

其中内核里的相关define如下(也可在此网址查询各类标识符含义:Index of all identifiers in unit ‘BaseUnix’ (freepascal.org)):

#define S_IRUGO		(S_IRUSR|S_IRGRP|S_IROTH)
#define S_IWUGO		(S_IWUSR|S_IWGRP|S_IWOTH)
#define S_IXUGO		(S_IXUSR|S_IXGRP|S_IXOTH)
... ...
#define S_IRWXU 00700 //const S_IRWXU = S_IRUSR or S_IWUSR or S_IXUSR;用户读写执行
#define S_IFDIR  0040000 //mode: Directory
#define S_IRUSR 00400 //用户读
#define S_IRGRP 00040 //用户组读
#define S_IROTH 00004 //其他读
#define S_IWUSR 00200 //用户写
#define S_IWGRP 00020 //用户组写
#define S_IWOTH 00002 //其他写
#define S_IXUSR 00100 //用户执行
#define S_IXGRP 00010 //用户组执行
#define S_IXOTH 00001 //其他执行
... ...

如此,知道了debugfs_create_dir里赋予新目录文件的权限是:目录+用户xwr+组xwr+其他xwr
这个权限值就对应着ls -l后各个文件的权限值

再来看debugfs_create_file,它的函数里调用了__debugfs_create_file函数,在__debugfs_create_file里:inode->i_private = data;//data就对应着调用debugfs_create_file时的private_hd指针

这一句将传进去的驱动自定义结构体指针private_hd存到i_private 里,这样子在后续对文件进行读写操作时,将这个i_private 的值传进读、写操作函数,就可在函数内操作private_hd中的值,从而实现修改内核中参数的功能。

在驱动中加入ops操作集如下:

static const struct file_operations number_debugfs_ops = {
    .write  = number_debugfs_write,      
    .read   = number_debugfs_read,
    .open   = simple_open,
    .llseek = generic_file_llseek,
};

其中file_operations结构:

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 *);
    ... ...
};

在驱动中加入number文件的读写操作函数如下,传入参数表的格式照着file_operations中定义的格式写:

static ssize_t number_debugfs_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos){
        struct my_handle *priv=file->private_data;
        char buf[32];
        int ret;
        ssize_t read;
        printk("number_read:%d\n", priv->number);
        ret=scnprintf(buf, sizeof(buf)-1, "number=%d\n", priv->number);
        if(*ppos >= ret)    return 0;//这里是为了第二次进入read函数时,如果文件位置指针已经超过了目的指针位置,则说明上次函数已读取字符串并打印到控制台里,直接退出即可
        read=copy_to_user(user_buf, (void *)buf, ret);//正常情况均返回0,则read赋值为0
        *ppos += ret;//移动文件读取位置指针
        return ret;
}

static ssize_t number_debugfs_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos){
        struct my_handle *priv=file->private_data;
        char buf[32];
        int val;
        ssize_t len=min_t(size_t, count, sizeof(buf)-1);
        if(copy_from_user(buf, user_buf, len))
                return -EFAULT;
        buf[len]='\0';
        if(sscanf(buf, "%d", &val)>0)
                priv->number=val;
        printk("after number_write:%d\n", priv->number);
        return count;
}

其中,读写函数传入参数表里的loff_t *ppos表示的是这次对文件进行操作的起始位置。

推荐阅读