• 正文
  • 相关推荐
申请入驻 产业图谱

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

02/10 11:45
734
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

1. 前言

很多朋友在调试驱动的时候,都会遇到这样一个场景:修改一个参数,然后调用某个内核中的函数。

比如将某个gpio的值拉高/拉低,修改某个寄存器的值等等。

如果每一个参数都通过字符设备的ioctl接口,增加对应的cmd,会比较麻烦,研究内核的计算机大牛门怎么会容忍这种事发生,于是设计出了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:%dn",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?<?0){
??dev_err(&pdev->dev,?"could?not?create?sysfs?filesn");
??ret?=?-ENOENT;
?}

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

4. 源码

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

完整源码如下:

device.c

#include?<linux/init.h>
#include?<linux/module.h>
#include?<linux/platform_device.h>
#include?<linux/ioport.h>
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?<linux/init.h>
#include?<linux/module.h>
#include?<linux/kdev_t.h>
#include?<linux/fs.h>
#include?<linux/cdev.h>
#include?<linux/device.h>
#include?<asm/io.h>
#include?<linux/platform_device.h>
#include?<linux/ioport.h>


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:%dn",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?<?0){
??dev_err(&pdev->dev,?"could?not?create?sysfs?filesn");
??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. 编译运行

第一步:编译

第二步:加载模块驱动第三步:查看生成的文件节点:

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

echo?1?>?peng

由结果可知,我们通过向文件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)

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

?driversinputtouchscreenads7846.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,?"%un",?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,?"%un",?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

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

相关推荐

登录即可解锁
  • 海量技术文章
  • 设计资源下载
  • 产业链客户资源
  • 写文章/发需求
立即登录

公众号『一口Linux』号主彭老师,拥有15年嵌入式开发经验和培训经验。曾任职ZTE,某研究所,华清远见教学总监。拥有多篇网络协议相关专利和软件著作。精通计算机网络、Linux系统编程、ARM、Linux驱动、龙芯、物联网。原创内容基本从实际项目出发,保持原理+实践风格,适合Linux驱动新手入门和技术进阶。