linux驱动之debugfs使用

linux驱动之debugfs使用

代码

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表示的是这次对文件进行操作的起始位置。

运行效果

运行后效果:能看到在指定目录phy3下,成功创建了my_test目录,并在my_test目录里创建了文件number。读写number的时候,会调驱动里的操作函数,能通过dmesg看到对应输出。

不过很神奇的是,经多次实践发现,一次读操作会调两次驱动中的文件读函数,而number_debugfs_read的返回值将决定是否有第二次进入以及是否能将字符串打印到控制台。

  • 如果调用它直接返回0,则不会将“number=x”打印到控制台,返回正数值就会再次进入函数;
  • 如果每次调用它都返回正数值(例如返回值是ret的值,每次进入函数ret都不可能被赋值为0),则会一直进入此函数形成死循环,在控制台狂打印“number=x”;
  • 若第一次调用它返回正数值,第二次进入时,通过条件判断是第二次进入,从而使第二次返回0,即可在控制台打印一次“number=x”,并结束调用

希望以上这部分有懂得大佬能解释下!!!在这里插入图片描述
如果ls -l一下,能看到:

root@vostro:/sys/kernel/debug/ieee80211/phy3/my_test# ls -l
总用量 0
-rw------- 1 root root 0 826 20:50 number

表示文件的拥有者root对文件有读写权限,组用户和其他用户没有任何权限

TIPS

正好我有写过如何在一个应用层软件代码里获取当前wifi驱动对应的debugfs目录phy值,分享一下(前提是指定wlan1)

获取每次驱动对应的phy值:

int GetPhy(void)
{
    int phy=0;
    FILE *tmpfd;
    char string[256];
    char* str;
    CMD_EXEC("iw %s info | grep wiphy | cut -f2 -dw > /tmp/tmp_phy.txt", WFA_STAUT_IF);//WFA_STAUT_IF是一个全局变量,指定了wifi驱动的网口为wlan1
    tmpfd = fopen("/tmp/tmp_phy.txt", "r+");
    if(tmpfd == NULL) { return FAILURE;}
    for(;;){
            if(fgets(string, 256, tmpfd) == NULL)
                break;
            if(strncmp(string, "iphy", 4) ==0)
            {
                DPRINT_INFO("--> Found phy info \n");
                str = strtok(string, " ");
		DPRINT_INFO("1:%s\n", str);
                str = strtok(NULL, " ");
		DPRINT_INFO("2:%s\n", str);
                if(str != NULL){
			DPRINT_INFO("PHYID=%s\n", str);
			phy = atoi(str);	
		}
                else
                    phy = 0;
            }
    }
    DPRINT_INFO("phyid == %d--\n", phy);
    return phy;
}

其实原理就是一句命令:

len@vostro:~$ iw wlan1 info | grep wiphy
    wiphy 3
len@vostro:~$
  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`debugfs` 是一个用于调试文件系统的工具,可以用来查看和修改文件系统的元数据,包括 inode、块、目录等信息。它通常用于恢复损坏的文件系统或者进行文件系统的调试和分析。 以下是 `debugfs` 常用的一些命令和用法: 1. 打开文件系统 ``` debugfs /dev/device ``` 这个命令会打开 `/dev/device` 设备对应的文件系统,以便进行调试操作。请注意,使用 `debugfs` 命令需要以 root 用户身份运行。 2. 查看 inode 节点 ``` debugfs: inode <inode> ``` 这个命令会查看指定 inode 节点的详细信息,包括文件类型、权限、大小、数据块列表等。 3. 查看目录内容 ``` debugfs: ls <directory> ``` 这个命令会列出指定目录下的所有文件和子目录。 4. 查看数据块内容 ``` debugfs: dump <block> ``` 这个命令会查看指定数据块的内容。请注意,这里的数据块编号是逻辑块编号,不是物理块编号。 5. 查找文件名对应的 inode 节点 ``` debugfs: ncheck <inode> ``` 这个命令会查找指定 inode 节点对应的文件名和路径。 6. 查找数据块对应的文件名和 inode 节点 ``` debugfs: icheck <block> ``` 这个命令会查找指定数据块所属的文件名和 inode 节点。 7. 修改 inode 节点的属性 ``` debugfs: set_inode_field <inode> <field> <value> ``` 这个命令会修改指定 inode 节点的属性。其中,`<field>` 可以是 `mode`、`uid`、`gid`、`atime`、`ctime`、`mtime`、`size` 等字段。 8. 修改数据块的内容 ``` debugfs: write <block> <file> ``` 这个命令会将指定文件的内容写入指定数据块中。 请注意,使用 `debugfs` 命令需要非常小心,因为不正确的操作可能会损坏文件系统。建议在进行任何修改之前先备份文件系统。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值