良许Linux教程网 干货合集 Linux驱动小技巧 | 利用DRIVER_ATTR实现调用内核函数

Linux驱动小技巧 | 利用DRIVER_ATTR实现调用内核函数

1. 导言

在调试驱动程序时,许多朋友都会遇到一个常见场景:修改一个参数,然后调用内核中的某个函数。

例如,改变某个GPIO的状态(高电平/低电平),修改某个寄存器的值等等。

如果每个参数都需要通过字符设备的ioctl接口并添加相应的命令,将会相当繁琐。

对于研究内核的计算机专家来说,他们是不会容忍这种情况发生的。

因此,他们设计了DRIVER_ATTR这个宏,完美解决了这个需求。

接下来,通过一个简单的示例,我将向大家演示如何使用DRIVER_ATTR

2. DRIVER_ATTR定义

该宏定义的文件如下:include/linux/device.h

struct driver_attribute {

 struct attribute attr;

 ssize_t (*show)(struct device_driver *driver, char *buf);

 ssize_t (*store)(struct device_driver *driver, const char *buf,

    size_t count);

};

#define DRIVER_ATTR(_name, _mode, _show, _store) \

 struct driver_attribute driver_attr_##_name = __ATTR(_name, _mode, _show, _store)

__ATTR定义于文件 include/linux/sysfs.h

#define __ATTR(_name, _mode, _show, _store) {    \

 .attr = {.name = __stringify(_name), .mode = _mode },  \


 .show = _show,      \

 .store = _store,      \

}

说明

 _name:名称,也就是将在sys fs中生成的文件名称。

 _mode:上述文件的访问权限,与普通文件相同,UGO的格式,最高权限0644,否则会报错。


 _show:显示函数,cat该文件时,此函数被调用。

 _store:写函数,echo内容到该文件时,此函数被调用。

3. 使用步骤

定义一个写操作的回调函数:

static ssize_t peng_test_store(struct device_driver *driver,

     const char *buf, size_t count)

{
//对参数进行检查
 if(NULL == buf || count >255 || count == 0 || strnchr(buf, count, 0x20))
  return -1;

 printk("buf:%s count:%d\n",buf,count);

 return count;
}

声明该函数与文件节点关系

static DRIVER_ATTR(peng, 0644, NULL, peng_test_store);

创建文件节点:

 ret = driver_create_file(&(hello_driver.driver), &driver_attr_peng);
 if (ret dev, "could not create sysfs files\n");
  ret = -ENOENT;
 }

这几个名字之间关系如下:

4. 源码

本实验代码分为两个模块 device、driver, 分别定义结构体platform_device、platform_driver并注册到platform总线。

完整源码如下:

device.c

#include 
#include 
#include 
#include 
static void hello_release(struct device *dev)
{
     return;
}
static struct platform_device hello_device = 
{
 .name = "duang",
 .id = -1,
 .dev.release = hello_release,
};
static int hello_init(void)
{
 printk("hello_init \n");
 return platform_device_register(&hello_device);
  
}
static void hello_exit(void)
{
 printk("hello_exit \n");
 platform_device_unregister(&hello_device);
 return;
}
MODULE_LICENSE("GPL");
module_init(hello_init);
module_exit(hello_exit);

driver.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


static int hello_probe(struct platform_device *pdev);
static  int hello_remove(struct platform_device *pdev);


static ssize_t peng_test_store(struct device_driver *driver,
     const char *buf, size_t count)
{
 if(NULL == buf || count >255 || count == 0 || strnchr(buf, count, 0x20))
  return -1;

 printk("buf:%s count:%d\n",buf,count);

 return count;
}
static DRIVER_ATTR(peng, 0644, NULL, peng_test_store);

static struct platform_driver hello_driver =
{
 .probe = hello_probe,
 .driver.name = "duang",
 .remove = hello_remove,  
};

struct resource *res;
static int hello_probe(struct platform_device *pdev)
{
 int ret;
 printk("match ok \n");

 ret = driver_create_file(&(hello_driver.driver), &driver_attr_peng);
 if (ret dev, "could not create sysfs files\n");
  ret = -ENOENT;
 }

 
 return 0;
}
static  int hello_remove(struct platform_device *pdev)
{
 printk("hello_remove \n");
 return 0;
}

static int hello_init(void)
{
 printk("hello_init \n");
 return platform_driver_register(&hello_driver);
}
static void hello_exit(void)
{
 printk("hello_exit \n");
 platform_driver_unregister(&hello_driver);
 return;
}
MODULE_LICENSE("GPL");
module_init(hello_init);
module_exit(hello_exit);

Makefile

ifneq ($(KERNELRELEASE),)
obj-m:=device.o driver.o
else
KDIR :=/lib/modules/$(shell uname -r)/build
#KDIR :=/home/peng/linux-3.14
PWD  :=$(shell pwd)
all:
 make -C $(KDIR) M=$(PWD) modules
clean:
 rm -f *.ko *.o *.mod.o *.symvers *.cmd  *.mod.c *.order
endif

5. 编译运行

第一步:编译image-20240510225531959

第二步:加载模块驱动image-20240510225536580

第三步:查看生成的文件节点:

第四步:通过下面命令向节点输入一个数字(要管理员权限):

echo 1 > peng
image-20240510225545139
image-20240510225545139

由结果可知,我们通过向文件peng写入一个字符,实现了调用函数peng_test_store(),并且字符1传递给了参数buf,字符个数传递给了count

其中目录duang是由结构体变量hello_driver 给出:

static struct platform_driver hello_driver =
{
 .driver.name = "duang",
};

6. 一次注册多个节点

需要借助结构体

 struct attribute

以及函数

/**
 * sysfs_create_group - given a directory kobject, create an attribute group
 * @kobj: The kobject to create the group on
 * @grp: The attribute group to create
 *
 * This function creates a group for the first time.  It will explicitly
 * warn and error if any of the attribute files being created already exist.
 *
 * Returns 0 on success or error.
 */
int sysfs_create_group(struct kobject *kobj,
         const struct attribute_group *grp)

此处就不验证了,直接从内核找个例子给大家学习下吧

 drivers\input\touchscreen\ads7846.c
static ssize_t ads7846_pen_down_show(struct device *dev,
         struct device_attribute *attr, char *buf)
{
 struct ads7846 *ts = dev_get_drvdata(dev);

 return sprintf(buf, "%u\n", ts->pendown);
}

static DEVICE_ATTR(pen_down, S_IRUGO, ads7846_pen_down_show, NULL);

static ssize_t ads7846_disable_show(struct device *dev,
         struct device_attribute *attr, char *buf)
{
 struct ads7846 *ts = dev_get_drvdata(dev);

 return sprintf(buf, "%u\n", ts->disabled);
}

static ssize_t ads7846_disable_store(struct device *dev,
         struct device_attribute *attr,
         const char *buf, size_t count)
{
 struct ads7846 *ts = dev_get_drvdata(dev);
 unsigned int i;
 int err;

 err = kstrtouint(buf, 10, &i);
 if (err)
  return err;

 if (i)
  ads7846_disable(ts);
 else
  ads7846_enable(ts);

 return count;
}
static DEVICE_ATTR(disable, 0664, ads7846_disable_show, ads7846_disable_store);

static struct attribute *ads784x_attributes[] = {
 &dev_attr_pen_down.attr,
 &dev_attr_disable.attr,
 NULL,
};

static struct attribute_group ads784x_attr_group = {
 .attrs = ads784x_attributes,
};
 err = sysfs_create_group(&mydevice->dev.kobj, &ads784x_attr_group);

7. 补充

当然_ATTR不是独生子女,他还有一系列的姊妹__ATTR_RO宏只有读方法,__ATTR_NULL等等

如对设备的使用        DEVICE_ATTR   
对驱动使用               DRIVER_ATTR
对总线使用               BUS_ATTR 
对类别 (class) 使用  CLASS_ATTR

好了,大家后面在调试驱动的时候别忘了有这些宏可以使用。

以上就是良许教程网为各位朋友分享的Linu系统相关内容。想要了解更多Linux相关知识记得关注公众号“良许Linux”,或扫描下方二维码进行关注,更多干货等着你 !

137e00002230ad9f26e78-265x300
本文由 良许Linux教程网 发布,可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。
良许

作者: 良许

良许,世界500强企业Linux开发工程师,公众号【良许Linux】的作者,全网拥有超30W粉丝。个人标签:创业者,CSDN学院讲师,副业达人,流量玩家,摄影爱好者。
下一篇

发表评论

联系我们

联系我们

公众号:良许Linux

在线咨询: QQ交谈

邮箱: yychuyu@163.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

关注微博
返回顶部