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

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;}

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

bb0b13dc3f9d07571b4bc936d0f2941b.png

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. 编译运行

第一步:编译2deba6b7da14b6101f9cfbc682cb6e9a.png

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

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

echo 1 > peng

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/464644.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

myeclipse运行java项目_myeclipse运行自己从前的或其他人的javaweb项目

1.配置javaweb 连接部署好本地的tomcat服务器方法如下&#xff1a;1.运行Myecipse-->Window-->preference2.MyEclipse-->Servers-->Runtime Environments-->add-->Apache Tomcat v8.03.next->Browser(Tomcat的安装目录)-->finishMyeclipse 设置jdk版本…

MYSQL性能调优及架构设计学习笔记-影响MYSQL性能的相关因素之实例分析

为什么80%的码农都做不了架构师&#xff1f;>>> 需求概述 一个简单的讨论区系统&#xff0c;需要有用户&#xff0c;用户组&#xff0c;组讨论区这三部分基本功能 简要分析 1&#xff09; 须要存放用户数据的表&#xff1b; 2&#xff09; 须要存放分组信息和用户与…

洛谷P2089 烤鸡 题解

题目背景 猪猪hanke得到了一只鸡 题目描述 猪猪Hanke特别喜欢吃烤鸡&#xff08;本是同畜牲&#xff0c;相煎何太急&#xff01;&#xff09;Hanke吃鸡很特别&#xff0c;为什么特别呢&#xff1f;因为他有10种配料&#xff08;芥末、孜然等&#xff09;&#xff0c;每种配料可…

java excel 导入 加校验_POI实现excel各种验证和导入的思路总结

制定标准导入总是与导出相辅相成的&#xff0c;无规矩不成方圆。所谓的标准都是大家一同来维护和遵守的&#xff0c;那么首先就是制定一个模板。这样可以减少验证的工作量。例如时间的规范【yyyy-MM-dd】&#xff0c;获取单元格的时间值用下面的方法java.util.Date date cell.g…

在一个公司10年,会怎么样?

大家好&#xff0c;我是写代码的篮球球痴。最近在知乎上看到一个话题那些在一个公司死磕了5-10年的人最后都怎么样了&#xff1f;"在互联网红利爆发&#xff0c;人心躁动的今天&#xff0c;可以在一个公司磕到5~10年&#xff0c;真的很不容易&#xff0c;我记得前东家要是…

公钥私钥

公钥私钥 <?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office" />一&#xff0c;公钥私钥 1&#xff0c;公钥和私钥成对出现 2&#xff0c;公开的密钥叫公钥&#xff0c;只有自己知道的叫私钥 3&#xff0c;用公钥加密的数据只有…

软件测试第三次作业

一、开头 (1)合作者&#xff1a;201631062521&#xff0c;201631062421 (2)代码地址&#xff1a;https://gitee.com/h2503652646/WordCount.git (3)本次作业链接地址&#xff1a;https://edu.cnblogs.com/campus/xnsy/Test/homework/2203 二、正文 (1)互审代码情况 已经实现Wor…

equals()与==的区别

“”比较两个变量本身的值&#xff0c;即两个对象在内存中的首地址。“equals()”比较字符串中所包含的内容是否相同。 比如&#xff1a;String s1,s2,s3 "abc", s4 "abc" ;s1 new String("abc");s2 new String("abc");那么&#x…

Linux驱动开发中与设备树相关的6种debug方法

整理出了6种驱动开发时与设备注册、设备树相关的调试方法&#xff0c;彼此间没有优先级之分&#xff0c;每种方法不一定是最优解&#xff0c;但可以作为一种debug查找问题的手段&#xff0c;快速定位问题原因。例如在芯片验证时&#xff0c;不同时钟频率下系统启动情况摸底时&a…

hdu-1877(大数+进制转换)

题目链接&#xff1a;http://acm.hdu.edu.cn/showproblem.php?pid1877 思路&#xff1a;注意考虑0,0的情况。 #include<iostream> #include<cstdio> #include<string> #include<algorithm> using namespace std; int m,a,b; string s1,s2; string add…

多线程(一)

1) 什么是线程&#xff1f; 线程是操作系统能够进行运算调度的最小单位&#xff0c;它被包含在进程之中&#xff0c;是进程中的实际运作单位。程序员可以通过它进行多处理器编程&#xff0c;你可以使用多线程对运算密集型任务提速。比如&#xff0c;如果一个线程完成一个任务要…

DiscoveryService.getRemoteAdvertisements是否会获得本地通告?

从该方法名称上来说&#xff0c;应该是不能获得本地通告&#xff0c;本着探究的精神&#xff0c;首先做个小测试&#xff0c;再从源代码中寻找答案。----------------------------------------------------------------------------- 测试结果&#xff1a;本地通告确实取不到&a…

java ssh 和mvc_JAVA三大框架SSH和MVC

Java—SSH(MVC)JAVA三大框架的各自作用hibernate是底层基于jdbc的orm(对象关系映射)持久化框架&#xff0c;即&#xff1a;表与类的映射&#xff0c;字段与属性的映射&#xff0c;记录与对象的映射 数据库模型 也就是Model;struts提供的丰富标签是用于显示层View;同时struts也充…

Linux的bc命令计算π的值预估系统算力

这是今天突然想到的一个事情&#xff0c;几年前我和一个朋友做一个开发板&#xff0c;然后我们需要完成的这个开发板有算力的要求&#xff0c;当时我们测试的时候就用了一个shell脚本来分析系统的算力。今天我突然想不起这个命令&#xff0c;然后就想写篇文章记录下&#xff0c…

hotmail在outlook2007中的设置

在姓名中输入你的名字&#xff08;用户可自定义&#xff09;&#xff0c;在电子邮件地址中输入你要设置OutLook的电子邮件地址&#xff0c;账户类型设为POP3&#xff0c;接收邮件服务器为pop3.live.com&#xff0c;发送邮件服务器为smtp.live.com。在用户名中输入你的邮箱用户名…

第九章 图形用户界面的并行化(待续)

为什么GUI是单线程的转载于:https://www.cnblogs.com/hzzjj/p/9825864.html

SQL版DNN的安装心得

最重要的是,DNN必须在WEB HOST 的根目录。中国RainBow提醒的。这是装成的关键&#xff01;另外的主要两个问题&#xff0c;一是CONNECTIONSTRING中的PWD有&符号&#xff0c;出错。通过SQL 的企业管理器登陆REMOTE SQL SERVER修改PASSWORD。(应该试一试 Integrated Security…

面向对象的思想是什么?

我同事的回答&#xff0c;我觉得这句话可以读十遍说下我自己的理解你不是人&#xff0c;你是猪。解释一下我们在编写面向对象的代码时&#xff0c;一定要有抽象的思想&#xff0c;什么是抽象&#xff0c;抽象是一种概念的东西&#xff0c;不是实实在在的&#xff0c;看不见摸不…

java 数组正则表达式_java正则表达式实现提取需要的字符并放入数组【ArrayList数组去重复功能】...

本文实例讲述了java正则表达式实现提取需要的字符并放入数组。分享给大家供大家参考&#xff0c;具体如下&#xff1a;这里演示java正则表达式提取需要的字符并放入数组&#xff0c;即arraylist数组去重复功能。具体代码如下&#xff1a;package com.test.tool;import java.uti…

多线程(三)

38) 如何在Java中创建Immutable对象&#xff1f;要创建不可变类&#xff0c;要实现下面几个步骤&#xff1a;(1)、通过构造方法初始化所有成员&#xff1b;(2)、对变量不要提供set方法&#xff1b;(3)、将所有的成员声明为私有的&#xff0c;这样就不允许直接访问这些成员&…