Linux内核驱动注册方式泛谈

Linux驱动注册有多种方式,通常是以内核提供的表征数据结构封装后按照内核子系统提供的接口函数进行注册,还有一些是比较复杂的以链表方式进行维护。以下对几种驱动注册方式进行介绍:

一、子系统有专门的驱动注册函数:

例如RTC子系统,提供rtc_device_register注册接口函数。

例如:

rtc_device_register(client->name,&client->dev, &rx8025_rtc_ops, THIS_MODULE);

static struct rtc_class_ops rx8025_rtc_ops= {

.read_time= rx8025_get_time,

.set_time= rx8025_set_time,

.read_alarm= rx8025_read_alarm,

.set_alarm= rx8025_set_alarm,

.alarm_irq_enable= rx8025_alarm_irq_enable,

};

主要实现rtc_class_ops。使用RTC接口rtc_device_register,注册到RTC子系统中,受RTC子系统控制,包括设备创建,PROC和SYS文件系统属性创建。可以使用RTC子系统接口文件interface.c中的函数。

另外网口类型驱动也是有专门的接口:

register_netdev(ndev);

注册网口后受内核管理。

二、没有专门的驱动注册函数

这类驱动注册比较直接,按照通用的字符驱动函数进行。例如看门狗类驱动。采用misc类型驱动进行注册。

例如:

misc_register(&rc32434_wdt_miscdev);

static const struct file_operationsrc32434_wdt_fops = {

.owner               = THIS_MODULE,

.llseek                = no_llseek,

.write                 = rc32434_wdt_write,

.unlocked_ioctl         = rc32434_wdt_ioctl,

.open                 = rc32434_wdt_open,

.release   = rc32434_wdt_release,

};

实现看门狗自有的接口。

三、复杂驱动

该类驱动在内核配置中有专门的选项,一旦选中该选项,在内核启动后会执行相关系统模块初始化,维护一个链表,而具体驱动是往该链表中添加成员,系统模块会检测新成员加入并完成加入到子系统处理工作。

例如PCI:

PCI子系统在注册时启动BIOS:

subsys_initcall(pcibios_init);

其中subsys_initcall在内核初始化时进行调用。

关于subsys_initcall定义:

#define pure_initcall(fn)             __define_initcall("0",fn,0)

#define core_initcall(fn)             __define_initcall("1",fn,1)

#define core_initcall_sync(fn)        __define_initcall("1s",fn,1s)

#define postcore_initcall(fn)         __define_initcall("2",fn,2)

#definepostcore_initcall_sync(fn)    __define_initcall("2s",fn,2s)

#define arch_initcall(fn)             __define_initcall("3",fn,3)

#define arch_initcall_sync(fn)        __define_initcall("3s",fn,3s)

#define subsys_initcall(fn)           __define_initcall("4",fn,4)

#define subsys_initcall_sync(fn)      __define_initcall("4s",fn,4s)

#define fs_initcall(fn)               __define_initcall("5",fn,5)

#define fs_initcall_sync(fn)          __define_initcall("5s",fn,5s)

#define rootfs_initcall(fn)    __define_initcall("rootfs",fn,rootfs)

#define device_initcall(fn)           __define_initcall("6",fn,6)

#define device_initcall_sync(fn)      __define_initcall("6s",fn,6s)

#define late_initcall(fn)             __define_initcall("7",fn,7)

#define late_initcall_sync(fn)        __define_initcall("7s",fn,7s)

在链接脚本vmlinux.lds中有这么一段:

各段在内核文件中的位置:

当内核在配置是选中PCI总线子系统时,内核启动后即会执行subsys_initcall(pcibios_init);

该模块维护一个全局变量hose_list。并以链表管理的方式进行管理,一旦该链表有新成员添加即会使用PCI子系统对其进行处理,包括扫描,探测等。

该方式主要工作是要往hose_list链表中添加成员然后即可使用PCI子系统。以Powerpc为例,PCI流程示意图如下所示:

CPU在内核启动后初始化ARCH重要模块,其中添加一个PCI桥,桥对Powerpc 的PCI控制器资源解析并分配一个controller控制器,然后添加到hose_list链表中。完成注册工作。

而pci_32.c文件中是对BOIS的管理,其中对hose_list链表的管理,管理添加进行的成员。发现有成员注册进来即会使用PCI子系统进行相关处理。

类似的RapdiIO驱动:

RapidIO系统也是采用类似PCI的方式,在内核配置时选中RapidIO后系统运行即会启动RapidIO子系统。

在rio.c中:

int __devinit rio_init_mports(void)

{

struct rio_mport *port;

list_for_each_entry(port,&rio_mports, node) {

if(port->host_deviceid >= 0)

rio_enum_mport(port);

else

rio_disc_mport(port);

}

rio_init();

return 0;

}

device_initcall_sync(rio_init_mports);

其中device_initcall_sync就是上面讲到的初始化段。

#define device_initcall_sync(fn)      __define_initcall("6s",fn,6s)

执行这个初始化启动RapidIo子系统的条件是内核中有相关配置。

再以Powerpc为例,以下是添加到RapidIO子系统的过程:

其中,rio.c中维护rio-mports链表,检测是否有新成员注册进来,一旦有新成员注册进来即添加到rapidIO子系统中进行处理。

而fsl_rio.c文件中有subsys_initcall初始化,完成RapdidIo驱动注册,这部分的工作也是内核配置之后即可完成。而注册完之后按照Powerpc上的资源进行解析并添加mport到rio-mports链表中。完成注册。

RapidIo的注册过程与PCI的注册过程属于同一种类型的。都是内核在配置时选中相关支持启动子系统,子系统启动一个检测链表的工作。而具体的驱动需要往该链表中添加成员。

Linux内核驱动注册方式泛谈

时间: 2024-08-09 07:23:39

Linux内核驱动注册方式泛谈的相关文章

第02节:Linux 内核驱动中的指定初始化

2.1 什么是指定初始化 在标准 C 中,当我们定义并初始化一个数组时,常用方法如下: int a[10] = {0,1,2,3,4,5,6,7,8}; 按照这种固定的顺序,我们可以依次给 a[0] 和 a[8] 赋值.因为没有对 a[9] 赋值,所以编译器会将 a[9] 默认设置为0.当数组长度比较小时,使用这种方式初始化比较方便.当数组比较大,而且数组里的非零元素并不连续时,这时候再按照固定顺序初始化就比较麻烦了. 比如,我们定义一个数组 b[100],其中 b[10].b[30] 需要初始

Linux内核调试的方式以及工具集锦

CSDN GitHub Linux内核调试的方式以及工具集锦 LDD-LinuxDeviceDrivers/study/debug 本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可, 转载请注明出处, 谢谢合作 因本人技术水平和知识面有限, 内容如有纰漏或者需要修正的地方, 欢迎大家指正, 也欢迎大家提供一些其他好的调试工具以供收录, 鄙人在此谢谢啦 "调试难度本来就是写代码的两倍. 因此, 如果你写代码的时候聪明用尽, 根据定义, 你就没有能耐去调试它了.&qu

linux 内核驱动--Platform Device和Platform_driver注册过程

linux 内核驱动--Platform Device和Platform_driver注册过程 从 Linux 2.6 起引入了一套新的驱动管理和注册机制 :Platform_device 和 Platform_driver . Linux 中大部分的设备驱动,都可以使用这套机制 , 设备用 Platform_device 表示,驱动用 Platform_driver 进行注册. Linux platform driver 机制和传统的 device driver 机制 ( 通过 driver_

linux内核驱动模型

linux内核驱动模型,以2.6.32内核为例.(一边写一边看的,有点乱.) 1.以内核对象为基础.用kobject表示,相当于其它对象的基类,是构建linux驱动模型的关键.具有相同类型的内核对象构成内核对象集,用kset表示,内核对象集也包含自己的内核对象,从而组成层次化的结构.2.用sysfs文件系统导出到用户空间.内核中的所有内核对象组织成树状,以对象属性为叶子.通过sysfs文件系统,将用户空间对文件的读写操作转化为对内核对象属性的显示和保存方法.从而导出内核对象信息,并提供配置接口.

Linux内核驱动编程

Linux内核驱动编程 2015-02-12 驱动程序基础的东西这儿就不罗嗦了,百度上有更好的资料,此处我们只是注重实际用处. 下面我们开始写程序: 一.初步helloword程序 首先是来一个简单的hello. hello.c代码: 1 /****************************** 2 3 the first program 4 5 Hello World! 6 7 ******************************/ 8 9 #include <linux/mod

Unix/Linux环境C编程入门教程(12) openSUSECCPP以及Linux内核驱动开发环境搭建

1. openSUSE是一款优秀的linux. 2.选择默认虚拟机 3.选择稍后安装操作系统 4.选择linux  opensuse 5. 选择默认虚拟机名称 6.设置处理器为双核. 7.内存设置为2G 8. 选择网络地址转换 9.设置IO控制器 10. 选择默认磁盘类型 11.创建一个新的虚拟磁盘 12.设置磁盘大小 13.选择路径保存虚拟磁盘 14. 完成虚拟机创建 15.设置虚拟机 16.选择opensuse镜像 17.开启虚拟机 18.虚拟机启动 19.安装opensuse 20.安装程

Unix/Linux环境C编程新手教程(12) openSUSECCPP以及Linux内核驱动开发环境搭建

1. openSUSE是一款优秀的linux. watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaXRjYXN0Y3Bw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" /> 2.选择默认虚拟机 3.选择稍后安装操作系统 4.选择linux  opensuse 5. 选择默认虚拟机名称 6.设置处理器为双核. watermark/2/text/a

你为什么看不懂Linux内核驱动源码?

学习嵌入式Linux驱动开发,最核心的技能就是能够编写Linux内核驱动.深入理解Linux内核.而做到这一步的基础,就是你要看得懂Linux内核源码,了解其基本的框架和具体实现,了解其内核API的使用方法,然后才能根据自己的需求写出高质量的内核驱动程序. 说易行难,很多新人.甚至工作1-2年的开发者刚接触Linux内核时,别说写了,看内核代码可能都是一脸懵逼:明明是C语言,但是就是看不懂是什么意思,除了根据函数名.函数参数.函数的返回值以及注释,了解整个函数的基本功能外,一旦分析其细节,你会发

linux内核驱动学习指南

目录: 1.参考链接 1. 参考链接 小白的博客 ONE_Tech 你为什么看不懂Linux内核驱动源码? 求教怎么学习linux内核驱动 原文地址:https://www.cnblogs.com/agui125/p/10071452.html