linux设备驱动的linux iic总线驱动,设备和驱动的关系

没有更多推荐了,
加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!1. 在\kernel\of\fdt.c 中有如下初始化函数
注释上:展开设备树,创建device_nodes到全局变量allnodes中void __init unflatten_device_tree(void){
__unflatten_device_tree(initial_boot_params, &allnodes,
early_init_dt_alloc_memory_arch);
/* Get pointer to "/chosen" and "/aliasas" nodes for use everywhere */
of_alias_scan(early_init_dt_alloc_memory_arch);}unflatten_device_tree函数被setup_arch函数调用,因为我们使用得是arm平台所以存在\kernel\arch\arm\kernel\setup.c中void __init setup_arch(char **cmdline_p){unflatten_device_tree()}setup_arch函数在kernel启动是被调用,如下启动kernel存在\kernel\init\main.c中 asmlinkage void __init start_kernel(void){
setup_arch(&command_line);}这些工作完成解析DTS文件。保存到全局链表allnodes中。2、在makefile中有这段话来编译dts文件:$(obj)/A20%.dtb: $(src)/dts/A20%.dts FORCE
$(call if_changed_dep,dtc)$(obj)/A68M%.dtb: $(src)/dts/A68M%.dts FORCE
$(call if_changed_dep,dtc)和.c文件生成.o文件一样 回生成.dtb文件。在/home/liyang/workspace/SZ_JB-mr1-8628-bsp-1012/out/target/product/msm8226/obj/KERNEL_OBJ/arch/arm/boot目录下,与zimage一个目录。3、在 board-8226.c中有初始化函数--&启动自动掉用void __init msm8226_init(void){
of_platform_populate(NULL, of_default_bus_match_table, adata, NULL);}of_platform_populate在kernel\driver\of\platform.c中定义,回查询
root = root ? of_node_get(root) : of_find_node_by_path("/");
for_each_child_of_node(root, child)
rc = of_platform_bus_create(child, matches, lookup, parent, true);
of_node_put(root);在这里用到得函数of_find_node_by_path会最终调用到kernel\driver\of\base.c中得函数struct device_node *of_find_node_by_path(const char *path){
遍历第1步中得allnodes找到根节点} of_platform_bus_create()函数中创建得内容存在了 adata中。(2)使用DTS注册总线设备的过程
以高通8974平台为例,在注册i2c总线时,会调用到qup_i2c_probe()接口,该接口用于申请总线资源和添加i2c适配器。在成功添加i2c适配器后,会调用of_i2c_register_devices()接口。此接口会解析i2c总线节点的子节点(挂载在该总线上的i2c设备节点),获取i2c设备的地址、中断号等硬件信息。然后调用request_module()加载设备对应的驱动文件,调用i2c_new_device(),生成i2c设备。此时设备和驱动都已加载,于是drvier里面的probe方法将被调用。后面流程就和之前一样了。简而言之,Linux采用DTS描述设备硬件信息后,省去了大量板文件垃圾信息。Linux在开机启动阶段,会解析DTS文件,保存到全局链表allnodes中,在掉用.init_machine时,会跟据allnodes中的信息注册平台总线和设备。值得注意的是,加载流程并不是按找从树根到树叶的方式递归注册,而是只注册根节点下的第一级子节点,第二级及之后的子节点暂不注册。Linux系统下的设备大多都是挂载在平台总线下的,因此在平台总线被注册后,会根据allnodes节点的树结构,去寻找该总线的子节点,所有的子节点将被作为设备注册到该总线上。
浏览: 11755 次
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'深度解析Linux SPI总线和设备驱动架构
SPI是&Serial Peripheral Interface& 的缩写,是一种四线制的同步串行通信接口,用来连接微控制器、传感器、存储设备,SPI设备分为主设备和从设备两种,用于通信和控制的四根线分别是:
&&CS & &片选信号
&&SCK &时钟信号
&&MISO &主设备的数据输入、从设备的数据输出脚
&&MOSI &主设备的数据输出、从设备的数据输入脚
通常,负责发出时钟信号的设备我们称之为主设备,另一方则作为从设备,下图是一个SPI系统的硬件连接示例:
图1.1 & &SPI硬件结构图
如上图所示,主设备对应SOC芯片中的SPI控制器,通常,一个SOC中可能存在多个SPI控制器,像上面的例子所示,SOC芯片中有3个SPI控制器。每个控制器下可以连接多个SPI从设备,每个从设备有各自独立的CS引脚。每个从设备共享另外3个信号引脚:SCK、MISO、MOSI。任何时刻,只有一个CS引脚处于有效状态,与该有效CS引脚连接的设备此时可以与主设备(SPI控制器)通信,其它的从设备处于等待状态,并且它们的3个引脚必须处于高阻状态。
按照时钟信号和数据信号之间的相位关系,SPI有4种工作时序模式:
我们用CPOL表示时钟信号的初始电平的状态,CPOL为0表示时钟信号初始状态为低电平,为1表示时钟信号的初始电平是高电平。另外,我们用CPHA来表示在那个时钟沿采样数据,CPHA为0表示在首个时钟变化沿采样数据,而CPHA为1则表示要在第二个时钟变化沿来采样数据。内核用CPOL和CPHA的组合来表示当前SPI需要的工作模式:
&&CPOL=0,CPHA=1 & & & &模式0
&&CPOL=0,CPHA=1 & & & &模式1
&&CPOL=1,CPHA=0 & & & &模式2
&&CPOL=1,CPHA=1 & & & &模式3
在内核的SPI驱动的软件架构中,进行了合理的分层和抽象,如下图所示:
图2.1 & &SPI驱动的软件架构
SPI控制器驱动程序
SPI控制器不用关心设备的具体功能,它只负责把上层协议驱动准备好的数据按SPI总线的时序要求发送给SPI设备,同时把从设备收到的数据返回给上层的协议驱动,因此,内核把SPI控制器的驱动程序独立出来。SPI控制器驱动负责控制具体的控制器硬件,诸如DMA和中断操作等等,因为多个上层的协议驱动可能会通过控制器请求数据传输操作,所以,SPI控制器驱动同时也要负责对这些请求进行队列管理,保证先进先出的原则。
SPI通用接口封装层
为了简化SPI驱动程序的编程工作,同时也为了降低协议驱动程序和控制器驱动程序的耦合程度,内核把控制器驱动和协议驱动的一些通用操作封装成标准的接口,加上一些通用的逻辑处理操作,组成了SPI通用接口封装层。这样的好处是,对于控制器驱动程序,只要实现标准的接口回调API,并把它注册到通用接口层即可,无需直接和协议层驱动程序进行交互。而对于协议层驱动来说,只需通过通用接口层提供的API即可完成设备和驱动的注册,并通过通用接口层的API完成数据的传输,无需关注SPI控制器驱动的实现细节。
SPI协议驱动程序
上面我们提到,控制器驱动程序并不清楚和关注设备的具体功能,SPI设备的具体功能是由SPI协议驱动程序完成的,SPI协议驱动程序了解设备的功能和通信数据的协议格式。向下,协议驱动通过通用接口层和控制器交换数据,向上,协议驱动通常会根据设备具体的功能和内核的其它子系统进行交互,例如,和MTD层交互以便把SPI接口的存储设备实现为某个文件系统,和TTY子系统交互把SPI设备实现为一个TTY设备,和网络子系统交互以便把一个SPI设备实现为一个网络设备,等等。当然,如果是一个专有的SPI设备,我们也可以按设备的协议要求,实现自己的专有协议驱动。
SPI通用设备驱动程序
有时候,考虑到连接在SPI控制器上的设备的可变性,在内核没有配备相应的协议驱动程序,对于这种情况,内核为我们准备了通用的SPI设备驱动程序,该通用设备驱动程序向用户空间提供了控制SPI控制的控制接口,具体的协议控制和数据传输工作交由用户空间根据具体的设备来完成,在这种方式中,只能采用同步的方式和SPI设备进行通信,所以通常用于一些数据量较少的简单SPI设备。
原文标题:Linux SPI总线和设备驱动架构你都懂了吗?
文章出处:【微信号:gh_c472c2199c88,微信公众号:嵌入式ARM】欢迎添加关注!文章转载请注明出处。
发布评论请先
管道是由内核管理的一个缓冲区,相当于我们放入内存中的一个纸条。管道的一端连接一个进程的输出。这个进程....
本文主要介绍了嵌入式Linux系统开发教程及案例分析。
FreeRTOS内核支持优先级调度算法,每个任务可根据重要程度的不同被赋予一定的优先级,CPU总是让....
本文主要介绍了嵌入式linux开发详细资料。
GNOME 开发团队刚刚宣布了一个好消息,因为有位神秘人士承诺会分两年时间、向非盈利性质的 GNOM....
在近 30 年的发展中,Linux 先后衍生出了众多发行版本。比如妇孺皆知的 Android,还有开....
Linux 操作系统最初是在 1991 年 10 月份由芬兰赫尔辛基大学的在校生 Linus Tor....
实际上,很多已经被我们习以为常的C语言标准函数,在Linux平台上的实现都是靠系统调用完成的,所以如....
推特中包含的一张照片显示了运行Debian的Switch,黑客团队说这个漏洞攻击是针对启动ROM中的....
创建一个进程时,它的第一个线程称为主线程(Primary thread),由系统自动生成。然后可以由....
目前嵌入式处理器常见的有ARM、PowerPC、MIPS、Motorola 68K、ColdFire....
strcpy: 最常用的字符串拷贝函数,但是要注意这个函数不会自己判断源字符串是否比目标空间大,必须....
Xenomai正是充分利用了Adeos技术,它的首要目标是帮助人们尽量平缓地移植那些依赖传统RTOS....
迅为IMX6开发板丨Android丨Ubuntu丨Linux丨三大系统全开源。
Linux服务器的性价比有多高?举例来说,某集成电路设计中心机房的IC设计服务器是2004年采购,采....
我们有一个工具正好可以做到在单个 U 盘上保留多个 Linux 发行版。你所需要做的只是选择要安装的....
为了保护每个人的隐私和工作环境,针对某一个文档(文件、目录),Linux系统定义了三种身份,分别是拥....
ls命令用于列出文件和目录,默认列出当前目录的内容。带上参数后,我们可以用ls做更多的事情。这里是一....
昨天有报道称,联想在中央某采购中心关于预装国产操作系统的投票会上投了反对票一事属实。联想昨晚回应记者....
最后是每个程序的资源占用列表,其中 CPU 的使用率是所有 CPU core 占用率的总和。通常执行....
本文首先介绍了ARM7、ARM9和uCOS及Linux这四者的概念,其次阐述了ARM7与ARM9的区....
该设计运用三星公司的S3C2440,结合ICRoute公司的高性能语音识别芯片LD3320,进行了语....
静态分配设备号,就是驱动程序开发者,静态地指定一个设备号。对于一部分常用的设备,内核开发者已经为其分....
本文档提供了OMAP35X Linux PSP包的一部分的设备驱动程序的概述和性能数据。
如果你对 Linux 感兴趣,但是一直在使用 PC 或者 Mac 桌面,你可能想知道你需要知道什么才....
最全Linux TOP命令使用教程!
find 是日常工具箱中功能更强大、更灵活的命令行工具之一,因此值得花费更多的时间。
单片机与应用处理器的核心区别到底是什么呢?是核心主频的差异?还是Linux系统的支持?又或者是处理器....
作为一个驱动作者, 你可能发现你面对一个设备必须在它能支持工作前下载固件到它里面. 硬件市场的许多地....
红帽公布CoreOS与红帽 OpenShift集成的发展蓝图 CoreOS与红帽 OpenShift....
学习Linux的好帮手,对于大家学习一定帮助
这里ROM Code是上电后最先执行的动作,就相当一个启动引导的过程;U-Boot除了初始外设外,还....
可以看到,我们在使用kobject、kset、ktype结构,就在sysfs虚拟文件系统下创建(通过....
致力于在功耗、安全、可靠性和性能方面提供差异化的领先半导体技术方案供应商美高森美公司(Microse....
Linux操作系统下C语言编程入门
这仅仅只是一个例子。你甚至可以使用 Vim-anywhere 在烦人的 web 表单或者其他应用上进....
倪光南在反思芯片发展的问题时也提到:越没人用就越无法获得应用支持,越没有应用支持就越没人用,这是个恶....
ARM 公司推出的编译工具,功能和 arm-none-eabi 类似,可以编译裸机程序(u-boot....
内存溢出的解决办法:1、等比例缩小图片。2、对图片采用软引用,及时进行 recycle( ) 操作。....
Linux内核中设置了一组用于实现各种系统功能的子程序,称为系统调用。用户可以通过系统调用命令在自己....
作为一个驱动作者, 你可能发现你面对一个设备必须在它能支持工作前下载固件到它里面. 硬件市场的许多地....
Linux内核中设置了一组用于实现各种系统功能的子程序,称为系统调用。用户可以通过系统调用命令在自己....
有时候你很忙。而有时候你只是需要看起来很忙,就像电影中的黑客一样。有一些开源工具就是干这个的。
请注意,在单个CPU上读写锁比RCU慢一个数量级,在16个CPU上读写锁比RCU几乎要慢两个数量级。....
减少锁竞争的一个有效方法是创建一个分级结构,如上图所示。在此,四个rcu_node 结构中的每一个都....
本文档提供了OMAP35X Linux PSP包的一部分的设备驱动程序的概述和性能数据。
默认情况下,每当我们尝试使用 touch 命令更改符号链接文件的时间戳时,它只会更改原始文件的时间戳....
Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据....
如果你想要以快速且粗略地在局域网中一次监控多台主机,你可能需要了解一下 “rwho” 工具。只要安装....
供应链服务
版权所有 (C) 深圳华强聚丰电子科技有限公司
电信与信息服务业务经营许可证:粤B2-linux设备驱动模型第一次笔记_So_care_about_you__新浪博客
linux设备驱动模型第一次笔记
1.1Linux设备驱动模型简介
1、什么是设备驱动模型
(1)类class。总线bus(负责将设备和驱动挂接起来)。设备devices、驱动driver(可以看到在驱动源码中,不管是什么样的驱动,都是以struct_xxx_driver来表示的)。Linux设备驱动中的四个框架、分别对应Linux驱动源代码中的四个结构体。四个结构体分别描述Linux设备驱动中的类、总线、设备、驱动,这四个概念。对应的就是设备驱动模型这个概念了,四个模子。
(2)kobject和对象生命周期。kobject是Linux源代码中的一个结构体,高度抽象的结构体,就是Linux内核中所有对象高度抽象出来的类,也就是Linux面向对象中,一个总的父类。
Linux中是如何管理对象的生命周期呢。就是利用了kobject总类中的机制,每个对象都有这种机制,因为kobject是linux中最高的父类,也就是基类,这种机制就会让每个对象能够具有自我管理生命周期的特性。
对象不用时,自己会将自己free掉,就跟调用了析构函数一样。这就是Linux内核虽然是用C写的,但是是面向对象的含义所在。
(3)sysfs,一种虚拟文件系统,作用是将应用层的空间和内核空间中的内容建立起了一个映射关系,就是内核中的一些结构体什么的信息值啊,在sysfs这个虚拟文件系统中以文件的形式展现出来,这样应用层就可以跟内核进行互动。比如在sysfs中的控制led灯的文件中,我们echo一个值进行就可以让led灯亮,这就是sysfs虚拟文件系统为我们提供的机制,让内核和应用层建立起了映射关系。
(4)udev,也是为了实现内核空间和用户空间(应用层)之间的通信,让用户空间可以及时的知道内核空间中发生的事情。比如某个驱动被加载了,或者被卸载了,在用户空间都可以体现,像可以在用户空间用lsmod查看。
什么是设备驱动模型呢,上面的四个东西()就是设备驱动模型。
1、2为什么需要设备驱动模型
1、早期内核(2.4版本之前)是没有统一的设备驱动模型的,但照样可以用。用法就是我们自己去insmod一个驱动,mknod一个设备文件、在驱动对象卸载时,rmmod去卸载,对应的的释放内存的那种方法。
2、2.6版本及以后就引入了设备驱动模型了,我们就可以去class_create,自动创建一个设备驱动等。因为设备越来越多了,需要管理了,所以需要有一个好的体系。所以为了好管理,我们在去写驱动的时候,就会去用设备驱动模型去写驱动了,就是调用框架中提供的成员函数去创建驱动,同时每一个驱动因为都继承自最终的父类kobject,所以都可以在自己驱动消亡的时候,自己知道去释放内存。说白了设备驱动模型就是一种规则,用这种规则去写驱动,可以很好的管理设备驱动,因为设备驱动太多了。
3、设备驱动模型统一实现和维护一些特性:如,电源管理、热插拔、对象自我管理自己的生命周期(内存的释放),用户空间和内核空间之间的信息交互等。
1、3整个Linux驱动开发中的两个点
1、驱动源码本身的编写、调试。重点在于对硬件要很了解,才能写这个硬件的驱动。这样的驱动一般都是厂家写的。
2、驱动什么时候被安装(当insmod时、这种用户层去手动安装的方式是非常老的了,当我们设备接入时驱动自动安装,开机时自动加载,这个是新的方式,也就是利用设备驱动模型去写的驱动。)、驱动中的函数什么时候被调用(在应用层怎么操作一下,就会对应调用驱动中的函数)。这部分和硬件无关,完全和设备驱动模型有关。设备驱动模型是Linux内核提供的,所以我们就是用设备驱动模型。
2、1、设备驱动模型的底层架构(三件套,三个结构体、kobject、kobj_type、kset)
1、kobject(总的结构体,最开始的,相当于父类,其他的结构体都是基于这个结构体构建起来的,所以可以把这个结构体看成一个基类,用C面向过程的角度来看,这个结构体是会被其他结构体包含的,是其他结构体的一个成员)
(1)kobject定义在include/linux/kobject.h中。
(2)是各种对象(总线、设备、驱动、类class等的对象)最基本的单元,提供一些公用型服务,如:对象引用计数、维护对象链表、对象上锁、对用户空间的表示。
(3)设备驱动模型中各种对象其内部都会包含一个kobject结构体,都继承自这个父类,kobject就是基类。
(4)总线、class类、设备、驱动、这几个抽象的类中都会继承kobject,所以kobject有的属性和行为,总线、class类、设备、驱动都会有。
(5)struct kobject {
const char*//来描述当前这个模型对象的名字
list_//用来将来维护对象链表,维护统一模型对象的链表,比如可以遍历驱动的所有对象、设备的所有对象、总线的、class的。
kobject*//上下层之间的挂接、不是平行的挂接,比如驱动的对象和总线的对象进行挂接,就需要这个成员变量来进行挂接。
kset*//对象上锁的功能。当用当前这个对象要进行敏感操作时,如果用总线的对象进行敏感操作,为了防止其他人用这个对象
//就需要对这个对象进行上锁,就可以用这个成员变量来进行,防止其他人用这个总线的对象,当自己操作完事之后在
//进行解锁。
struct kobj_type*//用来对对象在用户空间的表示的,将来在sysfs中能看到设备的驱动等。
struct sysfs_dirent*
//用来对对象进行引用计数的,当前的对象被别人用了几次,来统计该模型(总线、class、设备、驱动)的对象被他人用的次数。
//就是让自己记住,同时被几个人调用了。帮助维护对象的生命周期的,当当前这个对象的这个引用计数为0时,就表示没有东西
//在用当前这个对象了,没有用东西要靠当前这个对象来完成任务了,这时引用计数为0,就可以释放这个对象的内存了。
unsigned int state_initialized:1;
unsigned int state_in_sysfs:1;
unsigned int state_add_uevent_sent:1;
unsigned int state_remove_uevent_sent:1;
unsigned int uevent_suppress:1;
struct kref {//atomic_t是原子操作,就是操作是不可分割的。
atomic_//refcount,用来对对象的引用计数的,用了几个对象。
typedef struct {
} atomic_t;
(6)kobject相当于面向对象中的总的基类。总线、设备、class、驱动这些抽象的结构体中都会有这个kobject结构体。
2、kobj_type
(1)很多书中简称ktype,每一个kobject都需要绑定一个ktype来提供相应的功能,kobject这个总的基类中有一个成员变量为ktype,这个成员变量将来来绑定一个struct
kobj_type。
(2)绑定和包含的语义区别:包含指的是结构体肚子中有一个实实在在存在的成员,不需要我们来指定,内部本来就有了。绑定的意思是指,结构体内部中本来没有这个东西,但是提供了一个指向这个东西的指针,将来我们在用的时候的时候,需要手动的去绑定这个东西,就是将一个东西的指针赋值给这个指针,这样叫做绑定。
kobj_type的定义,凡是手动绑定了这个kobj_type结构体的kobject,都会有kobj_type中的功能,其实就相当于为了kobject提供了成员函数,只不过
这些成员函数最开始并不是在kobject中存在的,这些成员函数是存在一个kobj_type结构体中的,当我们需要了某些行为的时候,比如需要kobj_type中这些成员
函数功能时,就会手动的去让kobject中的struct kobj_type类型的指针变量去指向这个strct
kobj_type有相应成员函数的对象。达到是的kobject中有了相应的
成员函数的目的。将来在设备驱动模型(总线、设备、class、驱动)继承这个kobject时,也会有这些行为的存在。
struct kobj_type {
void (*release)(struct kobject
*kobj);//释放,如果kobject手动绑定了这个kobj_type,在这个函数中会去检查kobject中记录引用计数的那个成员
//,如果发现引用计数不为0,说明还有人在用当前对象,那么就会将引用计数减1,如果进到这个release函数
//中,发现引用计数为0了,说明现在是最后一个用这个对象的人也进行调用这个release函数进行释放对象了,
//则会将当前对象进行释放,内存释放,这也就是为什么这个函数的名字不用close,而用release的含义所在。
//close表示调用一次就会关闭所有的,但是release就没有close语义上那么强硬了,并不是调用一次就关闭所有
//的,而实际上是要看当前对象被引用的次数有没有变成0.
//看引用计数的方法很简单,因为函数参数是struct kobject *,所以通过这个参数就可以找到 struct
//而可以知道当前被引用的次数
const struct sysfs_ops *sysfs_
struct attribute **default_
const struct kobj_ns_type_operations *(*child_ns_type)(struct
kobject *kobj);
const void *(*namespace)(struct kobject *kobj);
So_care_about_you_
博客等级:
博客积分:0
博客访问:3,171
关注人气:0
荣誉徽章:本文转载自:
root@xiaobai-laptop:/sys# ll class/net/eth0
lrwxrwxrwx 1 root root 0
10:11 class/net/eth0 -& ../../devices/pci0:00:1c.5/.0/net/eth0/
。上面的图中,将
/*linux/device.h*/
51 struct bus_type {
52 const char *
53 struct bus_attribute *bus_
54 struct device_attribute *dev_
55 struct driver_attribute *drv_
57 int (*match)(struct device *dev, struct device_driver *drv);
58 int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
59 int (*probe)(struct device *dev);
60 int (*remove)(struct device *dev);
61 void (*shutdown)(struct device *dev);
63 int (*suspend)(struct device *dev, pm_message_t state);
64 int (*suspend_late)(struct device *dev, pm_message_t state);
65 int (*resume_early)(struct device *dev);
66 int (*resume)(struct device *dev);
68 struct dev_pm_ops *
70 struct bus_type_private *p;
红色部分是以后将会介绍的成员,其中name是总线的名字,&bus_attrs是总线的属性,那些函数指针的操作总线的方法,在这一章节先不讲总线的方法。
/*drivers/base/bus.c*/
865 int bus_register(struct bus_type *bus)
该调用有可能失败,所以必须检查它的返回值,如果注册成功,会在/sys/bus下看到指定名字的总线。
946 void bus_unregister(struct bus_type *bus)
/*8th_devModule_1/1st/bus.c*/
1 #include
2 #include
4 #include
6 struct bus_type usb_bus = {
7 .name = "usb", //定义总线的名字为usb,注册成功后将在/sys/bus目录下看到
8 }; //目录usb,如果你的系统已经有usb总线,那你就要换个名字。
10 static int __init usb_bus_init(void)
13 /*总线注册,必须检测返回值*/
14 ret = bus_register(&usb_bus);
15 if(ret){
16 printk("bus register failed!\n");
20 printk("usb bus init\n");
21 return 0;
24 static void __exit usb_bus_exit(void)
26 bus_unregister(&usb_bus);
27 printk("usb bus bye!\n");
30 module_init(usb_bus_init);
31 module_exit(usb_bus_exit);
33 MODULE_LICENSE("GPL");
[root: 1st]# insmod bus.ko
usb bus init
[root: 1st]# ls /sys/bus/usb/ //sys/bus目录下多了一个usb的目录,但是里面的东西都是空的,
devices drivers_autoprobe uevent //因为我仅仅设置了总线的名字
drivers drivers_probe
总线属性添加和删除:
个人理解,设置总线的属性后,会在对应的总线目录下增加了一个新的文件,通过对该文件的读写访问,触发相应的函数操作,从而实现/sys/的文件接口与内核设备模型的数据交互。
/*linux/sysfs.h*/
28 struct attribute {
29 const char * //设定该文件的名字
30 struct module * //设定该文件的属主
31 mode_ //设定该文件的文件操作权限
/*linux/device.h*/
38 struct bus_attribute {
40 ssize_t (*show)(struct bus_type *bus, char *buf);
41 ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count);
bus_attribute中有两个函数指针,show和store。
当访问总线目录中的name文件时,就会触发show函数,一般会将指定的信息存放到数组buf,并传到用户空间显示。
当修改总线目录中的name文件是,就会触发stroe函数,一般会将从用户空间传来的buf指针存放的count个字节内容存放到内核中。
由此可以看到,通过这样的文件,就能实现sys目录下的文件与内核设备模型之间的数据交互。
设置总线属性有两个步骤:
1、创建并初始化bus_attribute结构,使用宏BUS_ATTR
BUS_ATTR(_name, _mode, _show, _store)
该宏会定义一个名叫bus_attr__name(红色部分是固定的)的bus_attibute的结构,并且成员name设置为_name,文件权限mode设置为_mode,两个函数调用分别人show和store。
2、将bus_attibute添加到指定的总线上,使用以下调用:
/*/drivers/base/bus.c*/
123 int bus_create_file(struct bus_type *bus, struct bus_attribute *attr)
该函数失败时返回错误号。
一旦调用该函数,会就在指定bus总线的目录下新建一个名叫_name的文件,权限为_mode,当访问和修改该文件是会分别调用show和store函数调用。
如果不需要该属性时,使用以下函数删除:
/*/drivers/base/bus.c*/
135 void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr)
说了这么多,马上来个程序:
/*8th_devModule_1/2nd/bus.c*/
1 #include
2 #include
4 #include
6 #define VER_SIZE 100
8 struct bus_type usb_bus = {
9 .name = "usb",
12 char Version[VER_SIZE] =&"xiaobai V1.0";
14 static ssize_t&show_bus_version(struct bus_type *bus, char *buf)
16 return snprintf(buf, VER_SIZE, "%s\n", Version);
18 static ssize_t&store_bus_version(struct bus_type *bus, const char *buf, size_t count)
20 return snprintf(Version, VER_SIZE, "%s", buf);
22 /*该宏会定义一个名叫bus_attr_version的bus_attribute的结构,并且成员name设置为
23 * version,文件权限mode设置为S_IRUGO|S_IWUGO,并设置show函数为
24 * show_bus_version,stror&函数为stroe_bus_version*/
25 static BUS_ATTR(version, S_IRUGO|S_IWUGO, show_bus_version, store_bus_version);
27 static int __init usb_bus_init(void)
31 /*总线注册*/
32 ret = bus_register(&usb_bus);
33 if(ret){
34 printk("bus register failed!\n");
35 goto err1;
37 /*为总线添加属性,调用成功后在/sys/bus/usb目录下有一个version的文件,权限为
38 * S_IRUGO|S_IWUGO,查看该文件时会调用函数show_bus_version,修改时调用store。*/
39 ret = bus_create_file(&usb_bus, &bus_attr_version);
40 if(ret){
41 printk("bus creat file failed!\n");
42 goto err2;
44 printk("usb bus init\n");
45 return 0;
48 bus_unregister(&usb_bus);
53 static void __exit usb_bus_exit(void)
55 bus_remove_file(&usb_bus, &bus_attr_version); //不调用这个也可以的,删除总线时会删除
56 bus_unregister(&usb_bus);
57 printk("usb bus bye!\n");
验证一下:
[root: 2nd]# insmod bus.ko
usb bus init
[root: 2nd]# ls /sys/bus/usb/
devices drivers_autoprobe uevent
drivers drivers_probe&version //多了一个version文件
[root: 2nd]# ls -l /sys/bus/usb/version&//文件的权限的可读可写(S_IRUGO|S_IWUGO)
-rw-rw-rw-&1 root root 4096 Oct 27 23:46 /sys/bus/usb/version
[root: 2nd]# cat /sys/bus/usb/version //读文件时触发show函数
xiaobai V1.0
[root: 2nd]# echo "haha"& /sys/bus/usb/version //写文件是触发store函数
[root: 2nd]# cat /sys/bus/usb/version
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
在最底层,linux系统中每个设备都用一个device结构的表示,如下,我省略掉部分成员:
/*linux/device.h*/
369 struct device {
370 struct klist klist_
371 struct klist_node knode_ /* node in sibling list */
372 struct klist_node knode_
373 struct klist_node knode_
374 struct device * //指定该设备的父设备,如果不指定(NULL),注册后的设备目录
375 ///在sys/device下
377 char bus_id[BUS_ID_SIZE]; /* position on parent bus */ //在总线生识别设备的字符串,
385 //同时也是设备注册后的目录名字。
386 struct bus_type * /* type of bus device is on */ //指定该设备连接的总线
387 struct device_driver * /* which driver has allocated this
388 device */ //管理该设备的驱动函数
389 void *driver_ /* data private to the driver */ //驱动程序的私有数据
392 struct dev_pm_
422 void (*release)(struct device *dev); //当给设备的最后一个引用被删除时,调用该函数
在注册一个完整的device结构前,至少定义parrent、bus_id、bus和release成员。但我接下来的程序仅仅定义了bus_id(指定目录的名字)、bus(对应的总线,不加也行)和release(这是必须的,不然卸载模块时会出错,不信自己试试)。
设备注册和注销:
与总线的注册一样:
1、定义结构体device。
2、调用注册函数:
int device_register(struct device *dev)
函数失败返回非零,需要判断返回值来检查注册是否成功。
设备注销函数:
void device_unregister(struct device *dev)
设备属性:
这个也是和总线属性差不多,不细讲:
设备相关的结构体和总线的类似,处理指定文件属性为,还定义了show和store两个函数。
/*linux/device.h*/
300 struct device_attribute {
302 ssize_t (*show)(struct device *dev, struct device_attribute *attr,
303 char *buf);
304 ssize_t (*store)(struct device *dev, struct device_attribute *attr,
305 const char *buf, size_t count);
设置设备属性有两个步骤:
1、创建并初始化device_attribute结构,使用宏DEVICE_ATTR
DEVICE_ATTR(_name, _mode, _show, _store)
该宏会定义一个名叫dev_attr__name(红色部分是固定的)的device_attibute的结构,并且成员name设置为_name,文件权限mode设置为_mode,两个函数调用分别人show和store。
2、将device_attibute添加到指定的设备上,使用以下调用:
/*drivers/base/core.c*/
430 int device_create_file(struct device *dev, struct device_attribute *attr)
该函数失败时返回错误号。
一旦调用该函数,会就在指定dev设备的目录下新建一个名叫_name的文件,权限为_mode,当访问和修改该文件是会分别调用show和store函数调用。
如果不需要该属性时,使用以下函数删除:
/*drivers/base/core.c*/
443 void device_remove_file(struct device *dev, struct device_attribute *attr)
讲了这么多,来个函数,和总线那个函数差不多,具体功能就是新建了一个执行名字(usb_device)的的设备目录,并且里面有一个属性文件version。
/*8th_devModule_1/3rd/device.c*/
1 #include
2 #include
4 #include
6 #define VER_SIZE 100
8 extern struct bus_type usb_
10 void usb_dev_release(struct device *dev)
12 printk("&release\n");
15 struct device usb_device = {
16 .bus_id = "usb_device",
17 .bus = &usb_bus, //指定该设备的总线,这样会在sys/bus/usb/device目录下有一个软连接
18 .release = usb_dev_release, //必须要都有release函数,不然卸载时会出错
12 char Version[VER_SIZE] = "xiaobai V1.0";
14 static ssize_t&show_device_version(struct device *dev,
15 struct device_attribute *attr, char *buf)
17 return snprintf(buf, VER_SIZE, "%s\n", Version);
19 static ssize_t&store_device_version(struct device *dev,
20 struct device_attribute *attr, const char *buf, size_t count)
22 return snprintf(Version, VER_SIZE, "%s", buf);
24 /*该宏会定义一个名叫dev_attr_version的device_attribute的结构,并且成员name设置
25 *&为version,文件权限mode设置为S_IRUGO|S_IWUGO,并设置show函数为
26 *show_device_version,&,stror函数为stroe_device_version*/
27 static DEVICE_ATTR(version, S_IRUGO|S_IWUGO,
28 show_device_version, store_device_version);
30 static int __init usb_device_init(void)
34 /*设备注册,注册成功后在/sys/device目录下创建目录usb_device*/
35 ret = device_register(&usb_device);
36 if(ret){
37 printk("device register failed!\n");
38 goto err1;
40 /*为设备添加属性,调用成功后在/sys/device/usb_device/目录下有一个version的
41 *&文件,权限为S_IRUGO|S_IWUGO,查看该文件时会调用函数show_device_version,
42 *&修改时调用store_device_version。*/
43 ret = device_create_file(&usb_device, &dev_attr_version);
44 if(ret){
45 printk("device creat file failed!\n");
46 goto err2;
48 printk("usb device init\n");
49 return 0;
52 device_unregister(&usb_device);
57 static void __exit usb_device_exit(void)
59 device_remove_file(&usb_device, &dev_attr_version);
60 device_unregister(&usb_device);
61 printk("usb device bye!\n");
64 module_init(usb_device_init);
65 module_exit(usb_device_exit);
再看看效果,这是没有设置总线(bus = &usb_bus)时的效果,代码在8th_devModule_1/3rd/device_bak.c,我没有编译,如果需要的话自己编译运行看看:
[root: 3rd]# insmod device.ko
usb device init
[root: /]# find -name "usb_device" //注册后查找一下在哪里有以设备bus_id为名字的目录
./sys/devices/usb_device ///sys/device下有一个
[root: /]#
[root: /]# ls /sys/devices/usb_device/ //里面有一个空文件,一个属性文件version
uevent version
[root: /]# cat /sys/devices/usb_device/version //查看一下version,调用shoe函数
xiaobai V1.0
[root: /]# echo "haha" & /sys/devices/usb_device/version //修改version,调用store函数
[root: /]# cat /sys/devices/usb_device/version
下面的是程序8th_devModule_1/3rd/device.c的效果:
[root: /]# cd review_driver/8th_devModule/8th_devModule_1/3rd/
[root: 3rd]# insmod bus.ko //先加载总线的模块
usb bus init
[root: 3rd]# insmod device.ko //再加载设备的模块
usb device init
[root: 3rd]# cd /
[root: /]# find -name "usb_device" //查找一下usb_device,发现比没有指定总线时多了一个路径
./sys/devices/usb_device //这个目录的出现是因为语句(.bus = &usb_bus,)
./sys/bus/usb/devices/usb_device //这个目录的出现是因为语句(device_register(&usb_device);)
[root: /]# cat /sys/devices/usb_device/version
xiaobai V1.0
[root: /]# ls -l sys/bus/usb/devices //原来该路径只是一个软连接,是该设备归类到usb_bus总线下
lrwxrwxrwx 1 root root 0 Oct 27 13:49 usb_device -& ../../../devices/usb_device
[root: /]# rmmod device
release //下载时调用release函数调用,如果没有设置release,卸载时会出错。
usb device bye!
[root: /]# rmmod bus
usb bus bye!
[root: /]#
上面的验证需要先加载bus.ko,bus.c源程序和1st/bus.c的函数没什么区别,只是将usb_bus导出到符号表。不然的话,加载模块时不能找到对应的总线。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
四、驱动程序
设备模型跟踪所有系统所知道的设备。进行跟踪的主要原因是让驱动程序协调与设备之间的关系。
先看驱动程序的结构体,我仅仅贴出一些重要的成员:
/*linux/device.h*/
122 struct device_driver {
123 const char * //驱动函数的名字,在对应总线的driver目录下显示
124 struct bus_type * //指定该驱动程序所操作的总线类型,必须设置,不然会注册失败
126 struct module *
127 const char *mod_ /* used for built-in modules */
129 int (*probe) (struct device *dev); //探测函数,以后会讲
130 int (*remove) (struct device *dev); //卸载函数,当设备从系统中删除时调用,以后讲
131 void (*shutdown) (struct device *dev); //当系统关机是调用
132 int (*suspend) (struct device *dev, pm_message_t state);
133 int (*resume) (struct device *dev);
134 struct attribute_group **
136 struct dev_pm_ops *
138 struct driver_private *p;
和设备不一样的是,在注册驱动函数是必须指定该驱动函数对应的总线,因为驱动函数注册成功后,会存放在对应总线的driver目录下,如果没有总线,注册当然会失败。
与总线的注册一样:
1、定义结构体device_driver。
2、调用注册函数:
214 int driver_register(struct device_driver *drv)
函数失败返回非零,需要判断返回值来检查注册是否成功。
设备注销函数:
249 void driver_unregister(struct device_driver *drv)
驱动函数属性:
这个也是和总线属性差不多,不细讲:
驱动函数相关的结构体和总线的类似,处理指定文件属性为,还定义了show和store两个函数。
155 struct driver_attribute {
157 ssize_t (*show)(struct device_driver *driver, char *buf);
158 ssize_t (*store)(struct device_driver *driver, const char *buf,
159 size_t count);
设置设备属性有两个步骤:
1、创建并初始化device_attribute结构,使用宏DEVICE_ATTR
DRIVER_ATTR(_name, _mode, _show, _store)
该宏会定义一个名叫driver_attr__name(红色部分是固定的)的driver_attibute的结构,并且成员name设置为_name,文件权限mode设置为_mode,两个函数调用分别人show和store。
2、将device_attibute添加到指定的驱动函数上,使用以下调用:
/*drivers/base/driver.c*/
93 int driver_create_file(struct device_driver *drv, struct driver_attribute *attr)
该函数失败时返回错误号。
一旦调用该函数,会就在指定dev设备的目录下新建一个名叫_name的文件,权限为_mode,当访问和修改该文件是会分别调用show和store函数调用。
如果不需要该属性时,使用以下函数删除:
/*drivers/base/driver.c*/
110 void driver_remove_file(struct device_driver *drv, struct driver_attribute *attr)
贴上函数:
/*8th_devModule_1/4th/driver.c*/
1 #include
2 #include
4 #include
6 #define VER_SIZE 100
8 extern struct bus_type usb_
10 struct device_driver usb_driver = {
11 .name = "usb_driver",
12 .bus = &usb_bus,
15 char Version[VER_SIZE] = "xiaobai V1.0";
17 static ssize_t&show_driver_version(struct device_driver *drv, char *buf)
19 return snprintf(buf, VER_SIZE, "%s\n", Version);
21 static ssize_t&store_driver_version(struct device_driver *drv,
22 const char *buf, size_t count)
24 return snprintf(Version, VER_SIZE, "%s", buf);
26 /*该宏会定义一个名叫driver_attr_version的driver_attribute的结构,并且成员
27 * name设置为version,文件权限mode设置为S_IRUGO|S_IWUGO,并设置show函数为
28 * show_driver_version,stror函数为stroe_driver_version*/
29 static DRIVER_ATTR(version, S_IRUGO|S_IWUGO,
30 show_driver_version, store_driver_version);
32 static int __init usb_driver_init(void)
36 /*驱动注册,注册成功后在/sys/bus/usb/driver目录下创建目录usb_driver*/
37 ret = driver_register(&usb_driver);
38 if(ret){
39 printk("driver register failed!\n");
40 goto err1;
42 /*为驱动添加属性,调用成功后在/sys/bus/usb/dirver目录下有一个version的
43 *&文件,权限为S_IRUGO|S_IWUGO,查看该文件时会调用函数show_driver_version,修改时
44 *&调用store_driver_version。*/
45 ret = driver_create_file(&usb_driver, &driver_attr_version);
46 if(ret){
47 printk("driver creat file failed!\n");
48 goto err2;
50 printk("usb driver init\n");
51 return 0;
54 driver_unregister(&usb_driver);
59 static void __exit usb_driver_exit(void)
61 driver_remove_file(&usb_driver, &driver_attr_version);
62 driver_unregister(&usb_driver);
63 printk("usb driver bye!\n");
看看效果,同样必须先加载bus.ko:
[root: /]# cd review_driver/8th_devModule/8th_devModule_1/4th/
[root: 4th]# insmod bus.ko //必须先加载总线
usb bus init
[root: 4th]# insmod driver.ko
usb driver init
[root: 4th]# cd /
[root: /]# find -name "usb_driver" //只有一处创建了usb_driver目录
./sys/bus/usb/drivers/usb_driver
[root: /]# ls /sys/bus/usb/drivers/usb_driver/
bind uevent unbind version
[root: /]# cat /sys/bus/usb/drivers/usb_driver/version //访问version文件是触发show函数
xiaobai V1.0
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
这节讲得内容其实不多,归纳起来就是四个函数一个属性结构体。
属性结构体:xx_attribute。
注册函数:xx_register。
注销函数:xx+unregister。
创建属性文件函数:xx_create_file。
删除属性文件函数:xx_remove_file。
其中xx可以是总线(bus)、设备(device)或者驱动函数(deriver)。
一但注册成功,就会在/sys目录下相应的地方创建一个自己命名的目录。其中,设备和驱动函数还可以添加到指定的bus目录下。
总线的成功注册后会在/sys/bus目录下创建相应的目录。
设备的成功注册后会在/sys/device目录下创建相应的目录,如果指定总线,会在指定总线目录/sys/bus/xx/device下创建一个指向/sys/device目录的软连接。
驱动函数的公共注册会在/sys/bus/xx/driver目录下创建相应的目录。
属性文件提供了shoe和store两个函数调用,当读写文件时会触发相应的函数调用,实现内核sysfs与用户空间的数据交互。
三者具体的关系就在后面章节介绍。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
阅读(...) 评论()}

我要回帖

更多关于 linux i2c总线驱动 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信