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

第一步:编译

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

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

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)

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

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);elseads7846_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

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

end

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

  1. linux使用小技巧——screen

    linux使用小技巧--screen 在linux的日常应用中,经常会编译安装一些比较大的软件.有时也会编译安装内核.此时,我们大多会使用CRT或XSHELL等软件远程连接到linux服务器进行编译安 ...

  2. Linux驱动开发必看详解神秘内核(完全转载)

    Linux驱动开发必看详解神秘内核 完全转载-链接:http://blog.chinaunix.net/uid-21356596-id-1827434.html IT168 技术文档]在开始步入Lin ...

  3. (49)逆向分析KiSystemService/KiFastCallEntry调用内核函数部分(SST,SSDT,SSPT)

    一.回顾 前两篇博客,我逆向分析了 KiSystemService 和 KiFastCallEntry 填充_KTRAP_FRAME 结构体的代码,二者大同小异,主要的区别是 sysenter 只改了 ...

  4. 嵌入式Linux系统小技巧之U盘自动运行脚本

    上次说了一个脚本小技巧(预留启动脚本),这次说的这个小技巧,是U盘自动运行脚本. U盘自动运行脚本的用处 U盘自动运行脚本,也属于是系统预留的一个接口吧.当系统忘记密码.没有debug调试线.网络进不 ...

  5. linux操作小技巧

    总结一下平时能用得上的小技巧 一.自动补全要输入的命令 比如以下代码: ls test ->reactNative.js ->jsdnsjdajkdnsakj.php 这种情况下,想要打开 ...

  6. Linux驱动开发必看详解神秘内核

    I 在开始步入Linux设备驱动程序的神秘世界之前,让我们从驱动程序开发人员的角度看几个内核构成要素,熟悉一些基本的内核概念.我们将学习内核定时器.同步机制以及内存分配方法.不过,我们还是得从头开始这 ...

  7. linux应用调用内核函数,Hooking linux内核函数(一):寻找完美解决方案

    前言 我们最近参与了一个Linux系统安全相关项目,需要hooking几个重要的Linux内核函数调用,例如打开文件和启动进程,并利用它来启用系统活动监控并抢先阻止可疑进程. 最后,我们发明了一种有效 ...

  8. Linux下dvi驱动名字,vga/dvi/hdmi采集卡linux驱动支持到最新2.6.38内核

    经过VGAIC畅通员工的不懈努力,目前VGAIC采集卡(VGA/DVI/HDMI)已提供最新2.6.38内核linux系统的驱动.从而实现国内首家运行在2.6.38内核Linux系统的VGA采集卡,D ...

  9. shell 死循环if判断_运维小技巧(2):shell函数

    shell函数是什么 shell函数可以看作是一组shell命令的组合,用来完成一个特定的功能,它的功能和java中的方法类似. 2. shell函数长什么样 铛铛铛,先上代码. function l ...

最新文章

  1. php多选框怎么传值,tp3.2如何处理多选框传参和判断状态
  2. ZOJ1002 Fire Net(非递归版)
  3. linux下mysql修改root密码
  4. 用 GDI 操作 EMF 文件[8]: 绘制图元文件时改变画笔与画刷
  5. C++改变基类成员在派生类中的访问属性
  6. 提交自己开发的MR作业到YARN上运行的步骤
  7. VUE项目中 获得多个复选框 checkbox 选中的值(jquery)+ 解决 Uncaught TypeError: Cannot read property ‘push‘ of undefine
  8. LeetCode 219. 存在重复元素 II(哈希)
  9. 自由软件不够吸引人?
  10. 测试人员,到底要如何才能胜任软件测试工作?
  11. Tomcat基础教程(三)
  12. jsSIP-demo(完整源码加注释)
  13. 【车牌识别】基于matlab GUI BP神经网络车牌识别(带语音播报)【含Matlab源码 668期】
  14. 支付宝APP支付 错误代码 insufficient-isv-permissions 错误原因: ISV权限不足
  15. 配置win10系统服务器失败怎么解决,windows10系统重置失败如何解决
  16. 二叉树的遍历(递归算法与非递归算法)
  17. [论文笔记] 大型车牌检测数据集CCPD 阅读笔记
  18. 特征可视化技术(CAM)
  19. 【c#视频】——面向对象——多态
  20. Kinect开发之PowerPoint播放助手

热门文章

  1. python爬虫从入门到放弃(六)之 BeautifulSoup库的使用
  2. RequestMapping
  3. [转帖]关于win7共享的问题和解答
  4. VMware HA与VMotion的部署与搭建
  5. GNS3从入门到精通
  6. 企业应该如何选型ERP?
  7. java8中LocalDate、LocalTime、LocalDateTime介绍
  8. java中 flush()方法
  9. 链表的头结点和尾节点的用处
  10. 【笔试记录】2021/3/10阿里