深入理解Linux总线驱动模型 (linux 总线驱动模型)

Linux总线驱动模型是Linux内核中一个非常重要的概念。作为开源操作系统的代表,Linux在设备驱动方面的处理非常有特色,总线驱动模型就是Linux内核中的一部分。本文从何为总线驱动模型、总线的作用、驱动的作用、总线驱动接口以及驱动程序的实现等几个方面来详细介绍Linux总线驱动模型。

一、何为总线驱动模型

总线驱动模型是Linux内核的一个重要概念,位于内核层,其主要作用是方便设备驱动程序的编写。在此之前,Linux内核的驱动都是以硬件上的设备名为基础的,但是这种驱动方式会由于硬件设备的变化而引起问题,同时也会让代码结构变得复杂。所以,在Linux 2.2内核中引入了总线驱动模型。

总线驱动模型是通过抽象出设备与设备驱动程序之间的通用接口,然后对不同类型的设备和设备驱动进行分类,最终简化和统一设备驱动的实现。这样,不同类型的设备可以通过相同的接口进行管理和控制,这为深度开发和应用提供了便利。

二、总线的作用

在总线驱动模型中,总线是一个重要的概念,总线可以理解为连接设备与系统的桥梁,它提供了设备通信的接口,同时还可以通过总线控制设备的工作状态。在Linux内核驱动程序中,与总线相关的工作都由总线驱动程序完成。

总线包括了很多种类型,如I2C、PCI、USB、SPI、ACPI等等,每种总线都有各自的特点和使用场景。总线驱动程序是为了兼容各种总线所编写的程序,可以强制处理总线设备支持的各种特性和协议,为设备驱动程序和用户应用程序提供一个通用的接口。

三、驱动的作用

在总线驱动模型中,驱动起着非常重要的作用,它一般是由硬件设备厂商所编写的与设备相关的程序,其主要作用是实现设备的初始化、寄存器的读写、中断处理、数据传输等操作,以及向内核注册设备并接收外部命令。

与传统的驱动相比,Linux总线驱动更加抽象,通过总线来封装底层硬件的操作,从而减少了驱动程序的开发难度。

驱动的实现分为两部分,之一步是实现设备的适配,即将具体设备与内核进行衔接,与初始化代码接轨,以便能够获取设备的相关信息和掌握设备的状态。第二步是实现设备的操作,即通过具体的驱动函数对设备进行读写操作等。

四、总线驱动接口

总线驱动接口是总线驱动程序与设备驱动程序之间的桥梁,它包括了若干个函数指针,这些函数指针定义了总线驱动程序中各种设备驱动的接口,并允许设备驱动程序来“注册”在总线上的设备。

总线驱动接口包括以下函数指针:

probe函数:当内核检测到设备时,此函数将被调用。此函数应提供一种方法来识别设备,并将此设备与其驱动程序之间的关系建立起来。

remove函数:当设备从系统中移除时,此函数将被调用。此函数要负责删除驱动程序和设备之间的关系,并做好所有用完驱动程序和设备使用后需要进行的清理工作。

suspend函数和resume函数:当系统进入睡眠时,内核将调用suspend函数,以允许驱动程序保存所有状态信息,然后调用resume函数来恢复它。

shutdown函数:此函数是一个非常重要的函数,因为它负责将系统所有的设备注销并停止。在系统关机之前,内核通过此函数对所有设备进行关闭,并知道如何对设备进行清理。

五、驱动程序的实现

在实现驱动程序之前,需要先确认设备的硬件信息,这样才能确定等下实现时要用到哪些函数和方法。此外,还需要了解设备所用的总线类型和协议。

驱动程序的实现一般分为三个部分:

初始化:在此部分,驱动程序需要通过填充struct device结构体来向系统注册设备,这通常是通过调用device_register函数来完成的。同时,驱动程序还需要设置中断、开启DMA等等。

读写:驱动程序需要向CPU和设备发送数据或接收数据。为此,驱动程序需要实现一些函数来完成这些基本的任务。对于读写操作,一般需要实现open、close、read和write函数。

释放:当不再需要设备时,需要释放所有已申请的物理资源并撤销该设备的注册状态以及清理任何未释放的内存。

总线驱动模型是Linux内核的一个重要概念。总线、驱动程序和总线驱动接口三者共同形成一个完整的总线驱动模型。在Linux内核中,总线驱动模型的应用使得设备驱动程序编写更加方便,许多之前需要手动处理的驱动部分现在已经转移到内核中。因此,存储与网络等设备的调试和优化工作实现起来也会变得更加容易。

相关问题拓展阅读:

Linux驱动中probe函数何时被调用

最近看到linux的设备驱动模型,关于Kobject、Kset等还不是很清淅。看到了struct device_driver这个结闷渗构时,想到一个问题:它的初始化函数到底蚂袭脊在哪里调用呢?以前搞PCI驱动时用pci驱动注册函数就可以调用它,搞s3c2410驱动时只要在mach-dk2410.c中的struct platform_device *dk2410_devices {}中加入设备也会调用。但从来就没有想过具体的驱动注册并调用probe的过程。

于是打开SourceInsight追踪了一下:

从driver_register看起:

复制代码

int driver_register(struct device_driver * drv)

{

klist_init(&drv->klist_devices, klist_devices_get, klist_devices_put);

init_completion(&drv->unloaded);

return bus_add_driver(drv);

}

复制代码

klist_init与init_completion没去管它,可能是2.6的这个设备模型要做的一些工作。直觉告诉我要去bus_add_driver。

bus_add_driver中:

都是些Kobject 与 klist 、attr等。还是与设备模型有关的。但是其中有一句:

driver_attach(drv);

单听名字就很像:

void driver_attach(struct device_driver * drv)

{

bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);

}

这个熟悉,遍历总线上的设备并设用__driver_attach。

在__driver_attach中又主要是这样:

driver_probe_device(drv, dev);

跑到driver_probe_device中去看看:

有一段很重要:

if (drv->bus->match && !drv->bus->match(dev, drv))

goto Done;

明显,是调用的驱动的总线上的match函数。如果返回1,则可以继续,否则就Done了。

继承执行的话:

if (drv->probe) {

ret = drv->probe(dev);

if (ret) {

dev->driver = NULL;

goto ProbeFailed;

}

只要probe存在则调用之。至此就完成了probe的调用。

这个过程链的关键还是在drv->bus->match ,因为其余的地方出错的话就是注册失败,而只要注册禅毕不失败且match返回1,那么就铁定会调用驱程的probe了。你可以注册一个总线类型和总线,并在match中总是返回 1, 会发现,只要struct device_driver中的bus类型正确时,probe函数总是被调用.

PCI设备有自己的总线模型,估计在它的match中就有一个判断的条件。

复制代码

static int pci_bus_match(struct device *dev, struct device_driver *drv)

{

struct pci_dev *pci_dev = to_pci_dev(dev);

struct pci_driver *pci_drv = to_pci_driver(drv);

const struct pci_device_id *found_id;

found_id = pci_match_device(pci_drv, pci_dev);

if (found_id)

return 1;

return 0;

}

复制代码

再往下跟踪就知道主要是根据我们熟悉的id_table来的。

—另解

从driver_register看起,此处我的这里是:

复制代码

int driver_register(struct device_driver * drv)

{

if ((drv->bus->probe && drv->probe) ||

(drv->bus->remove && drv->remove) ||

(drv->bus->shutdown && drv->shutdown)) {

printk(KERN_WARNING “Driver ‘%s’ needs updating – please use bus_type methods\n”, drv->name);

}

klist_init(&drv->klist_devices, NULL, NULL);

return bus_add_driver(drv);

}

复制代码

klist_init不相关,不用管他,具体再去看bus_add_driver:

复制代码

int bus_add_driver(struct device_driver *drv)

{

//1.先kobject_set_name(&drv->kobj, “%s”, drv->name);

//2.再kobject_register(&drv->kobj)

//3.然后调用了:driver_attach(drv)

}

复制代码

int driver_attach(struct device_driver * drv)

{

return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);

}

真正起作用的是__driver_attach:

复制代码

static int __driver_attach(struct device * dev, void * data)

{

if (!dev->driver)

driver_probe_device(drv, dev);

}

int driver_probe_device(struct device_driver * drv, struct device * dev)

{

//1.先是判断bus是否match:

if (drv->bus->match && !drv->bus->match(dev, drv))

goto done;

//2.再具体执行probe:

ret = really_probe(dev, drv);

}

复制代码

really_probe才是我们要找的函数:

复制代码

static int really_probe(struct device *dev, struct device_driver *drv)

{

//1.先是调用的驱动所属总线的probe函数:

if (dev->bus->probe) {

ret = dev->bus->probe(dev);

if (ret)

goto probe_failed;

} else if (drv->probe) {

//2.再调用你的驱动中的probe函数:

ret = drv->probe(dev);

if (ret)

goto probe_failed;

}

}

复制代码

其中,drv->probe(dev),才是真正调用你的驱动实现的具体的probe函数。

也就是对应此文标题所问的,probe函数此时被调用。

声明:以下主要内容参考自:

关于struct device_driver结构中的probe探测函数的调用

分类: linux系统知识

绿色通道: 好文要顶 关注我 收藏该文与我联系

h13

关注 – 1

粉丝 – 216

+加关注

2 0

(请您对文章做出评价)

« 上一篇:__init和__initdata

» 下一篇:Linux USB 鼠标驱动程序解析

posted on:22 h13 阅读(15859) 评论(2) 编辑 收藏

评论

#1楼:19 天地不仁

//1.先是调用的驱动所属总线的probe函数:

if (dev->bus->probe) {

ret = dev->bus->probe(dev);

if (ret)

goto probe_failed;

} else if (drv->probe) {

//2.再调用你的驱动中的probe函数:

ret = drv->probe(dev);

if (ret)

goto probe_failed;

}

这个不是先后的关系哦

linux属性att文件 sys怎么操作?

Sys文件系统是一个类似于proc文件系统的特殊文件系统,用于将系统中的设备组织成层次结构,并向用户模式程序提供详细的内核数据结构信息。其实,就是 在用户态可以通过对sys文件系统的访问,来看内核态的一些驱动或者设备等。

去/sys看一看,

localhost:/sys#ls

/sys/ block/ bus/ class/ devices/ firmware/ kernel/ module/ power/

Block目录:包含所有的块设备,进入到block目录下,会发现下面全是link文件,link到sys/device/目录下的一些设备。

Devices目录:包含系统所有的设备,并根据设备挂接的总线类型组织成层次结构

Bus目手皮中录:包含系统中所有的总线类型

Drivers目录:包括内核中所有已注册的设备驱动程序

Class目录:系统中的设备类型(如网卡设备,声毕山卡设备等)。去class目录中看

一下,随便进到一个文件夹下,会发现该文件夹下的文件其实是连接文件,link到/sys/device/…/../…下的一个设备文件。

可以说明,其实class目录并不会新建什么设备,只是将已经注册的设备,在class目录下重新归类,放在一起。

1,在sys下,表示一个目录使用的结构体是 Kobject,但是在linux的内核中,有硬件的设备 和 软件的驱动,在sys下都需要用一个目录来表示。 单纯的一个Kobject结构无法表示完全,增加了容器,来封装Kobject。 即下面要将的:device和drive_device结构。

2,

更底层驱动目录的上一层目录,从sys角度上来说,他依然是个目录,所以他也有Kobjec这个变量。但是从他的意义上讲,他将

一些有公共特性Kobjec  的

device/driver_device结构组织到了一起,所以除了有Kobject这个变量外,他又添加了一些变量,组成了Kset这个结构来表示这

一级的目录。但是仅仅是用Kset来表示了这一级的目录,和1,一样,仅仅表示一个目录是不够的,在linux内核中,需要他在软件上有个映射。所以,也

将Kset进行了封装,形成了 

bus_type这个结构。

3, kobject在Kset的目录下,那么 device/device_driver 就在 bus_type结构下。所以,linux驱动模型中,驱动和设备都是挂在总线下面的。

4, 如上所述,Kset的意义:表示一个目录(由结构体下的Kobject来完成),并且这个目录下的所有目录有共同的特性(所以说,Kset表示的目握腊录下,不一定非要是Kobject街头的,也可以是Kset结构的。即:Kset嵌套Kset)。所以使用Kset来代替了以前的 subsystem结构。

网络遥控技术是指由一部计算机(主控端)去控制另一部计算机(被控端),而且当主控端在控制端时,就如同用户亲自坐在被控端前操作一样,可以执行被控端的应用程序,及使用被控端的系统资源。

VNC(Virtual Network Computing)是一套由ATT实验室所开发的可操控远程的计算机的软件,其采用了GPL授权条款,任何人都可免费取得该软件。VNC软件主要由两个部分组成:VNC server及VNC viewer。用户需先将VNC server安装在被控前亮端的计算机上后,才能在主控端执行VNC viewer控制被控端。

(在windows中也由一套著名的网络遥控软件――Symantec公司推出的pcAnywhere。

VNC server与VNC viewer支持多种操作系统,如Unix系列(Unix,Linux,Solaris等),windows及MacOS,因此可将VNC server 及VNC viewer分别安装在不同的操作系统中进行控制。如果目前操作的主控端计算机没有安装VNC viewer,也可以通过一般的网页浏览器来控制被控端。

整个VNC运行的工作流程悔慎如下:

(1) VNC客户端通过浏览器或VNC Viewer连接至VNC Server。

(2) VNC Server传送一对话窗口至客户端,要求输入连接密码,以及存取的VNC Server显示装置。

(3) 在客户端输机密码后,VNC Server验证客户端是否具有存取权限。

(4) 若是客户端通过VNC Server的验证,客户端即要求VNC Server显示桌面环境。

(5) VNC Server通过X Protocol 要求X Server将画面显示控制权交由VNC Server负责。

(6) VNC Server将来由 X Server的桌面环境利用VNC通信协议送至客户端,并且允许客户端控制VNC Server的桌面环境及输入装置。 『VNC的安装与使用』

本人的操作环境:被控端 Redhat9.0,主控端Windows XP。

1. 载VNC Server

VNC Server下载地址:

2.安装。

vnc-3.3.7-x86_linux.tar.gz (如果是源代码请看包里的说明),当下载了VNC的Linux版本后,可以解压文件到一个文件夹中,例如/home/vnc,然后复制文件vncserver、 vncpasswd和Xvnc到/usr/bin目录中。假如想要能够通过VNC服务器的整合Java界面远程控制Linux电脑,也得需去建立一个子目录/usr/local/vnc/classes。在建立此子目录之后,复制VNCJava.class文件到此目录中。(一般redhat 9.0自带以上文件,所以直接用终端执行就可以)。

3.在Linux上启动VNC Server

执行vncserver命令:

# vncserver

You will require a password to access your desktops.

Password: —-为了不想任何人都可以任意遥控此计算机。因此当第

Verify: —1次启动VNC server时,会要求设置网络遥控的密码。

New ‘X’ desktop is linux:1 —-一定要记住这一行稍后会用到。

Creating default startup script /root/.vnc/xstartup

Starting applications specified in /root/.vnc/xstartup

Log file is /root/.vnc/linux:1.log

(经上述步骤后,便已启动了VNC Server。如果你想要更改VNC Server的密码,只要执行vncpasswd命令即可。)

5. 从浏览器远程遥控。

启动VNC Server 后直接打开浏览器,在地址栏中输入被控端的网址或慧前宽IP地址,并在网址后加上“:5800+显示编号”的端口号即可操控该计算机。

例如: (如果显示编号为1,一般之一次设置的显示编号都是1,就用5800+1=5801。)

如果看到窗口,就说明你成功,在密码框输入密码,就能远程控制了。

6.FAQ

A. 如何以图形界面登录被控端?

编辑~/.vnc/xstartup

#!/bin/sh

# Uncomment the following two lines for normal desktop:

# unset SESSION_MANAGER

# exec /etc/X11/xinit/xinitrc

exec /etc/vnc/xstartup

xrdb $HOME/.Xresources

xsetroot -solid grey

vncconfig -iconic

#xterm -geometry 80×24+10+10 -ls -title “$VNCDESKTOP Desktop”

gnome-session

startkde

twm

gnome-session是启动Gnome图形界面;startkde是启动KDE图形界面;twm是启动文本界面。三者选其一。

A. 如何开机时自动启动vnc服务

打开“系统设置”–”服务器设置”–”服务”,勾选上vncserver,“保存”。

修改 /etc/sysconfig/vncservers

VNCSERVERS=”1:root 2:phred 3:sysadmin” 1代表“桌面1”,root代表用户名;可同时启动多个用户桌面

VNCSERVERARGS=”-geometry 800×600 -alwaysshared -depth 24〃

-alwaysshared代表允许多用户同时登录 -depth代为色深,参数有8,16,24,32。

A.为什么连接后,不能显示桌面,而只有一个Terminal窗口?

试着修改/root/.vnc/xstartup,把最后一行 twm 改成 gnome-session or kde(据说KDE在目前的VNC Viewer上的表现不太稳定)

B. 为什么重新启动VNC Server后,连接不上了?

因为重新启动VNC Server时,系统会指定一个新的显示编号,需使用此新的编号,否则就无法连接

——————————————————————————————————————————–

查看vnc已启用的桌面号及监听进程

# netstat -tlnp |grep vnc

vncserver 命令带有一些很有用的参数,用vncserver –help 查看。

简单解释一下:

vncserver

vncserver -kill :display

:

vnc服务的display号,可以自行指定,尽量不要使用系统默认的以保证安全,必须为非0的正整数

vncserver :99

使用时必须输入正确的display号才能连接;用浏览器要在端口号加上display号,默认端口5800,远程连接输入 XXX.XXX.XXX.XXX:5899

-name 指定vncserver桌面名字

比如要让名字显示为 “红帽”

vncserver -name 红帽

-geometry x指定显示桌面的分辨率,默认为1024×768

vncserver -geometry 800×600

这个命令实际中很有用,例如本地分辨率为1024×768 如果不设定远程vnc服务分辨率就会造成桌面显示不全的问题。

-depth 指定显示颜色,设定范围8~32

vncserver -depth 16

用16bits颜色显示

-pixelformat 指定色素格式 与-depth大致相同,只是表示方法不一样

vncserver -pixelformat RGB888

用24bits颜色显示

vncserver -kill :display

结束vnc服务及远程桌面

vncserver -kill :1

其它更详细的使用请用man查看。

vnc内定的窗口管理器是twm与大家使用的可能不同,通过修改vnc的配置文件可以更换为自己喜欢的,方法如下:

修改用户目录下的 .vnc/xstartup 文件

#!/bin/sh

xrdb $HOME/.Xresources

xsetroot -solid grey

vncconfig -iconic

xterm -geometry 80×24+10+10 -ls -title “$VNCDESKTOP Desktop”

twm

将#!/bin/sh后面的内容全部注释掉,改成

~/.Xclients

保存文件

现在可以测试修改是否成功,先kill vncserver服务

vncserver -kill :

pa aux|grep Xvnc

kill PID

重新启动vncserver ,用vncviewer连接远程桌面是不是已经变成自己喜欢的类型了。

在前面有vncserver设置的文章(http: //bbs.chinaunix.net/forum/viewtopic.php?t=368552highlight=yunqing) 里,我们已经可以在一个linux的服务器上运行vncserver来进行远程访问了,但需要注意的是,如果直接使用vncviewer来进行访问,有 两点不利因素:

1.口令传输是明文,很容易被侦听到.

2.防火墙需要打开59xx端口,这在通常的单位里是不可能的.

幸运的是,我们有ssh这个强大的工具,象X11Forwarding(另文论述),我们可以使用ssh隧道来保护通讯过程,下面就进行简单介绍.

假设vncserver运行在服务器myserver(ip地址为:192.168.x.x)的屏幕1,也就是侦听192.168.x.x:5901,用户名为foo

情形一:vnc客户端为linux

这种情形很简单,一般的linux发行版本里面都默认安装了ssh套件.

1.用ssh登录到服务器

ssh-L5901:localhost:5901-lfoomyserver 或者直接用ip地址

ssh-L5901:localhost:5901-lfoo192.168.x.x 2.在本机的另外开的终端上,运行vncviewer(或其它的类似工具)

关于linux 总线驱动模型的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。


数据运维技术 » 深入理解Linux总线驱动模型 (linux 总线驱动模型)