Linux设备驱动程序学习(14)

-Linux设备模型(各环节的整合)

通过一个设备在内核中生命周期的各个阶段,可以更好地理解Linux设备模型。我将通过分析lddbus和sculld的源码来了解Linux设备模型中各环节的整合。《LDD3》中的(PCI总线)各环节的整合这部分内容作为参考资料,因为嵌入式Linux比较少用到PCI总线。看这部分内容一定要先熟悉一下 lddbus 和 sculld 的源码。

一、lddbus模块:添加总线、导出总线设备和设备驱动的注册函数。

lddbus子系统声明了一个bus_type结构,称为ldd_bus_type 。源码是在编译时初始化了这个结构体,源码:

/*

* And the bus type.

*/

struct bus_type ldd_bus_type = {

.name = "ldd",

.match = ldd_match,

.uevent  = ldd_uevent,

};

在将lddbus子系统装载到内核和从内核卸载的源码如下:static int __init ldd_bus_init(void)

{

int ret;

ret = bus_register(&ldd_bus_type); /*注册总线,在调用这个函数之后ldd_bus_type 结构体将向内核注册,在/sys/bus中出现ldd文件夹,其中包含两个目录:devices 和 drivers*/

if (ret)

return ret;

if (bus_create_file(&ldd_bus_type, &bus_attr_version)) /*添加总线属性,将在/sys/bus/ldd目录中出现version属性文件*/

printk(KERN_NOTICE "Unable to create version attribute ! \n");

ret = device_register(&ldd_bus);/*将总线作为设备注册。因为总线也可以是一个设备,比如在S3C2440中SPI总线控制器相对于ARM920T核心来说,其实就是一个外设。调用此函数后,就会在/sys/devices中出现ldd0目录*/

if (ret)

printk(KERN_NOTICE "Unable to register ldd0 ! \n");

printk(KERN_NOTICE "Mount lddbus ok !\nBus device is ldd0 !\nYou can see me in sys/module/ , sys/devices/ and sys/bus/ ! \n");

return ret;

}

static void ldd_bus_exit(void)

{

device_unregister(&ldd_bus);

bus_unregister(&ldd_bus_type);

}

module_init(ldd_bus_init);

module_exit(ldd_bus_exit);

lddbus模块的主要部分就是这些,很简单。因为这只不过是一个虚拟的总线,没有实际的驱动。模块还导出了加载总线设备和总线驱动时需要用到的注册和注销函数。对于实际的总线,应该还要导出总线的读写例程。

将总线设备和驱动注册函数放在lddbus模块,并导出给其他的总线驱动程序使用,是因为注册总线设备和驱动需要总线结构体的信息,而且这些注册函数对于所有总线设备和驱动都一样。只要这个总线驱动一加载,其他的总线驱动程序就可以通过调用这些函数注册总线设备和驱动,方便了总线设备驱动的作者,减少了代码的冗余。

这些注册函数内部调用driver_register、device_register 和 driver_unregister、device_unregister 这些函数。

二、sculld模块:在scull的基础上添加设备和驱动注册和注销函数。

sculld模块基本和scull模块实现的功能一致,我参考《LDD3》提供的sculld,将以前实验过的功能较全的scull进行修改。主要的修改如下(其他还有些小改动):

//*******在源码的声明阶段添加如下代码,以增加设备和驱动的结构体*****

struct sculld_dev *sculld_devices; /* allocated in scull_init_module */

/* Device model stuff */

static struct ldd_driver sculld_driver = {

.version = "$Revision: 1.21-tekkamanninja $",

.module = THIS_MODULE,

.driver = {

.name = "sculld",

},

};

//**************************************************************

//******增加设备注册函数和设备号属性********************************static ssize_t sculld_show_dev(struct device *ddev, struct device_attribute *attr , char *buf)

{

struct sculld_dev *dev = ddev->driver_data;

return print_dev_t(buf, dev->cdev.dev);

}

static DEVICE_ATTR(dev, S_IRUGO, sculld_show_dev, NULL);

static void sculld_register_dev(struct sculld_dev *dev, int index)

{

sprintf(dev->devname, "sculld%d", index);

dev->ldev.name = dev->devname;

dev->ldev.driver = &sculld_driver;

dev->ldev.dev.driver_data = dev;

register_ldd_device(&dev->ldev);

if (device_create_file(&dev->ldev.dev, &dev_attr_dev))

printk( "Unable to create dev attribute ! \n");

}

//*****************************************************************

/*还要在模块的初始化函数和模块清除函数中添加设备和驱动的注册和注销函数*/

sculld_register_dev(sculld_devices + i, i);

register_ldd_driver(&sculld_driver);

unregister_ldd_device(&sculld_devices[i].ldev);

unregister_ldd_driver(&sculld_driver);

修改后好模块就可以实现向sysfs文件系统导出信息。

三、分析设备和驱动注册和注销核心函数,了解一般的注册、注销过程。

以下也参考了《LDD3》中的PCI驱动分析

(1)设备的注册

在驱动程序中对设备进行注册的核心函数是:int device_register(struct device *dev)

{

device_initialize(dev);

return device_add(dev);

}

在 device_register 函数中, 驱动核心初始化 device 结构体中的许多成员, 向 kobject 核心注册设备的 kobject ( 导致热插拔事件产生), 接着添加设备到其 parent 节点所拥有的设备链表中。此后所有的设备都可通过正确的顺序被访问, 并知道其位于设备层次中的哪一点。

设备接着被添加到总线相关的设备链表(包含了所有向总线注册的设备)中。接着驱动核心遍历这个链表, 为每个驱动程序调用该总线的match函数。

match函数主要是将驱动核心传递给它的 struct device 和 struct device_driver转换为特定的设备、驱动结构体 ,检查设备的特定信息, 以确定驱动程序是否支持该设备:

若不支持, 函数返回 0 给驱动核心,这样驱动核心移向链表中的下一个驱动;

若支持, 函数返回 1 给驱动核心,使驱动核心设置struct device 中的 driver 指针指向这个驱动, 并调用在 struct device_driver 中指定的 probe 函数.

probe 函数(又一次) 将驱动核心传递给它的 struct device 和 struct device_driver转换为特定的设备、驱动结构体 ,并再次验证这个驱动是否支持这个设备, 递增设备的引用计数, 接着调用总线驱动的 probe 函数:

若总线 probe 函数认为它不能处理这个设备,则返回一个负的错误值给驱动核心,这样驱动核心移向链表中的下一个设备;

若这个 probe 函数能够处理这个设备, 则初始化这个设备, 并返回 0 给驱动核心。这会使驱动核心添加设备到与这个特定驱动所绑定的设备链表中, 并在 /sys/bus的总线目录中的 drivers 目录中创建一个到这个设备符号链接(指向/sys/devices中的设备),使用户准确知道哪个驱动被绑定到了哪个设备。

(2)设备的注销

在驱动程序中对设备进行注销的核心函数是:

void device_unregister(struct device * dev)在 device_unregister 函数中, 驱动核心将删除这个设备的驱动程序(如果有)指向这个设备的符号链接, 并从它的内部设备链表中删除该设备, 再以 device 结构中的 struct kobject 指针为参数,调用 kobject_del。kobject_del 函数引起用户空间的 hotplug 调用,表明 kobject 现在从系统中删除, 接着删除所有该 kobject 以前创建的、与之相关联的 sysfs 文件和目录。kobject_del 函数也去除设备自身的 kobject 引用。此后, 所有的和这个设备关联的 sysfs 入口被去除, 并且和这个设备关联的内存被释放。

(3)驱动程序的注册

在驱动程序中对驱动程序进行注册的核心函数是:int driver_register(struct device_driver * drv)

driver_register 函数初始化 struct device_driver 结构体(包括 一个设备链表及其增删对象函数 和 一个自旋锁), 然后调用 bus_add_driver 函数。

bus_add_driver进行如下操作:

(1)查找驱动关联的总线:若未找到, 立刻返回负的错误值;

(2)根据驱动的名字和关联的总线,创建驱动的 sysfs 目录;

(3)获取总线的内部锁, 遍历所有的已经注册到总线的设备,为这些设备调用match函数, 若成功,进行剩下的绑定过程。(类似注册设备,不再赘述)

(4)驱动程序的注销删除驱动程序是一个简单的过程,在驱动程序中对驱动程序进行注销的核心函数是:

void driver_unregister(struct device_driver * drv)deiver_unregister 函数通过清理在 sysfs 树中连接到这个驱动入口的 sysfs 属性,来完成一些基本的管理工作。然后遍历所有属于该驱动的设备,为其调用 release 函数(类似设备从系统中删除时调用 release 函数)。

在所有的设备与驱动程序脱离后,通常在驱动程序中会使用下面两个函数:

down(&drv->unload_sem);

up(&drv->unload_sem);

它们在函数返回给调用者之前完成。这样做是因为在安全返回前,代码需要等待所有的对这个驱动的引用计数为 0。

模块卸载时,通常都要调用 driver_unregister 函数作为退出的方法。 只要驱动程序被设备引用并且等待这个锁时,模块就需要保留在内存中。这使得内核知道何时可以安全从内存删除驱动。

四、ARM9开发板实验

实验过程:

[Tekkaman2440@SBC2440V4]#insmod /lib/modules/lddbus.ko

Mount lddbus ok !

Bus device is ldd0 !

You can see me in sys/module/ , sys/devices/ and sys/bus/ !

[Tekkaman2440@SBC2440V4]#tree -AC /sys/module/lddbus/ /sys/devices/ldd0/ /sys/bus/ldd/

/sys/module/lddbus/

├── holders

├── initstate

├── refcnt

└── sections

├── __ksymtab

└── __ksymtab_strings

/sys/devices/ldd0/

├── power

│ └── wakeup

└── uevent

/sys/bus/ldd/

├── devices

├── drivers

├── drivers_autoprobe

├── drivers_probe

└── version

5 directories, 9 files

[Tekkaman2440@SBC2440V4]#cat /sys/bus/ldd/version

Revision: 1.9-tekkamanninja

[Tekkaman2440@SBC2440V4]#rmmod lddbus

The LDD bus device

ldd_bus_release : lddbus

[Tekkaman2440@SBC2440V4]#ls /sys/module /sys/devices /sys/bus

/sys/bus:

i2c ide mmc platform serio spi usb

/sys/devices:

platform system

/sys/module:

8250 loop rd snd_seq usbnet

atkbd mac80211 redboot snd_seq_oss v4l1_compat

cdrom mousedev rfd_ftl snd_soc_core vt

dm9000 nfs rtc_ds1307 snd_timer yaffs

hid ohci_hcd s3c2410_wdt spidev zc0301

ide_cd printk snd sunrpc

keyboard psmouse snd_pcm tcp_cubic

lockd rcupdate snd_pcm_oss usbcore

[Tekkaman2440@SBC2440V4]#insmod /lib/modules/sculld.ko

sculld: Unknown symbol register_ldd_device

sculld: Unknown symbol register_ldd_driver

sculld: Unknown symbol unregister_ldd_driver

sculld: Unknown symbol unregister_ldd_device

insmod: cannot insert '/lib/modules/sculld.ko': Unknown symbol in module (-1): No such file or directory

[Tekkaman2440@SBC2440V4]#insmod /lib/modules/lddbus.ko

Mount lddbus ok !

Bus device is ldd0 !

You can see me in sys/module/ , sys/devices/ and sys/bus/ !

[Tekkaman2440@SBC2440V4]#insmod /lib/modules/sculld.ko

[Tekkaman2440@SBC2440V4]#tree -AC /sys/module/lddbus/ /sys/devices/ldd0/ /sys/bus/ldd/ /sys/module/sculld/

/sys/module/lddbus/

├── holders

│ └── sculld -> ../../../module/sculld

├── initstate

├── refcnt

└── sections

├── __ksymtab

└── __ksymtab_strings

/sys/devices/ldd0/

├── power

│ └── wakeup

├── sculld0

│ ├── bus -> ../../../bus/ldd

│ ├── dev

│ ├── driver -> ../../../bus/ldd/drivers/sculld

│ ├── power

│ │ └── wakeup

│ ├── subsystem -> ../../../bus/ldd

│ └── uevent

├── sculld1

│ ├── bus -> ../../../bus/ldd

│ ├── dev

│ ├── driver -> ../../../bus/ldd/drivers/sculld

│ ├── power

│ │ └── wakeup

│ ├── subsystem -> ../../../bus/ldd

│ └── uevent

├── sculld2

│ ├── bus -> ../../../bus/ldd

│ ├── dev

│ ├── driver -> ../../../bus/ldd/drivers/sculld

│ ├── power

│ │ └── wakeup

│ ├── subsystem -> ../../../bus/ldd

│ └── uevent

├── sculld3

│ ├── bus -> ../../../bus/ldd

│ ├── dev

│ ├── driver -> ../../../bus/ldd/drivers/sculld

│ ├── power

│ │ └── wakeup

│ ├── subsystem -> ../../../bus/ldd

│ └── uevent

└── uevent

/sys/bus/ldd/

├── devices

│ ├── sculld0 -> ../../../devices/ldd0/sculld0

│ ├── sculld1 -> ../../../devices/ldd0/sculld1

│ ├── sculld2 -> ../../../devices/ldd0/sculld2

│ └── sculld3 -> ../../../devices/ldd0/sculld3

├── drivers

│ └── sculld

│ ├── bind

│ ├── sculld0 -> ../../../../devices/ldd0/sculld0

│ ├── sculld1 -> ../../../../devices/ldd0/sculld1

│ ├── sculld2 -> ../../../../devices/ldd0/sculld2

│ ├── sculld3 -> ../../../../devices/ldd0/sculld3

│ ├── unbind

│ └── version

├── drivers_autoprobe

├── drivers_probe

└── version

/sys/module/sculld/

├── holders

├── initstate

├── parameters

│ ├── scull_major

│ ├── scull_minor

│ ├── scull_nr_devs

│ ├── scull_qset

│ └── scull_quantum

├── refcnt

└── sections

├── __ex_table

└── __param

38 directories, 33 files

[Tekkaman2440@SBC2440V4]#cat /sys/bus/ldd/version /sys/devices/ldd0/sculld*/dev /sys/bus/ldd/drivers/sculld/version

Revision: 1.9-tekkamanninja

252:0

252:1

252:2

252:3

$Revision: 1.21-tekkamanninja $

[Tekkaman2440@SBC2440V4]#rmmod sculld

[Tekkaman2440@SBC2440V4]#tree -ACd /sys/module/lddbus/ /sys/devices/ldd0/ /sys/bus/ldd/ /sys/module/

/sys/module/lddbus/

├── holders

└── sections

/sys/devices/ldd0/

└── power

/sys/bus/ldd/

├── devices

└── drivers

/sys/module/

├── 8250

│ └── parameters

├── atkbd

│ └── drivers

│ └── serio:atkbd -> ../../../bus/serio/drivers/atkbd

├── cdrom

├── dm9000

│ └── parameters

├── hid

│ └── parameters

├── ide_cd

│ └── parameters

├── keyboard

│ └── parameters

├── lddbus

│ ├── holders

│ └── sections

├── lockd

│ └── parameters

├── loop

├── mac80211

│ └── parameters

├── mousedev

│ └── parameters

├── nfs

│ └── parameters

├── ohci_hcd

├── printk

│ └── parameters

├── psmouse

│ ├── drivers

│ │ └── serio:psmouse -> ../../../bus/serio/drivers/psmouse

│ └── parameters

├── rcupdate

├── rd

├── redboot

├── rfd_ftl

├── rtc_ds1307

├── s3c2410_wdt

├── snd

│ └── parameters

├── snd_pcm

│ └── parameters

├── snd_pcm_oss

│ └── parameters

├── snd_seq

│ └── parameters

├── snd_seq_oss

│ └── parameters

├── snd_soc_core

├── snd_timer

│ └── parameters

├── spidev

│ └── parameters

├── sunrpc

│ └── parameters

├── tcp_cubic

│ └── parameters

├── usbcore

│ ├── drivers

│ │ ├── usb:hub -> ../../../bus/usb/drivers/hub

│ │ └── usb:usbfs -> ../../../bus/usb/drivers/usbfs

│ └── parameters

├── usbnet

├── v4l1_compat

│ └── parameters

├── vt

│ └── parameters

├── yaffs

│ └── parameters

└── zc0301

├── drivers

│ └── usb:zc0301 -> ../../../bus/usb/drivers/zc0301

└── parameters

79 directories

[Tekkaman2440@SBC2440V4]#insmod /lib/modules/sculld.ko

[Tekkaman2440@SBC2440V4]#rmmod lddbus

rmmod: lddbus: Resource temporarily unavailable

[Tekkaman2440@SBC2440V4]#rmmod sculld

[Tekkaman2440@SBC2440V4]#rmmod lddbus

The LDD bus device

ldd_bus_release : lddbus

[Tekkaman2440@SBC2440V4]#

在系统中添加“tree”命令

实验中用到了Linux的常用命令“tree”,若使用busybox可能没有这个命令。如出现这种情况,可以下载此命令的源码,交叉编译一下,再放到根文件系统中的/bin目录中就好。在 CalmArrow 的博客中可以下载:http://blog.chinaunix.net/u/21948/showart_297101.html

(因为源码的下载地址 我一直进不去,在这里谢谢CalmArrow)

linux lddbus设备,Linux设备驱动程序学习(14)-Linux设备模型(各环节的整合)相关推荐

  1. 国嵌linux内核编程,国嵌视频学习笔记---linux内核开发1

    一.linux内核简介 1.linux系统由两部分组成:内核空间和用户空间. 2.用户空间包括:用户应用程序和C库 3.内核空间包括:系统调用接口.内核和体系结构相关代码. 4.ARM处理器7种工作模 ...

  2. 《马哥出品高薪linux运维教程》wingkeung学习笔记-linux基础入门课程5

    命令: 内部命令:由shell程序自带的命令叫做内部命令: 外部命令:在系统的某个路径下,有一个与命令同名的可执行程序叫做外部命令. 查看内外部命令的命令:type 命令 命令选项:用于调整命令执行行 ...

  3. linux设备驱动程序架构的研究,Linux设备驱动程序学习(12)-Linux设备模型(底层原理简介)...

    Linux设备驱动程序学习(12) -Linux设备模型(底层原理简介) 以<LDD3>的说法:Linux设备模型这部分内容可以认为是高级教材,对于多数程序作者来说是不必要的.但是我个人认 ...

  4. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之LED模板驱动程序的改造:设备树

    文章目录 前言 1.驱动的三种编写方法 2.怎么使用设备树写驱动程序 2.1.设备树节点要与platform_driver能匹配 2.2.修改platform_driver的源码 3.实验和调试技巧 ...

  5. 嵌入式linux/鸿蒙开发板(IMX6ULL)开发(三十二)LED模板驱动程序的改造:设备树

    文章目录 1.LED模板驱动程序的改造:设备树 1.1 总结3种写驱动程序的方法 1.2 怎么使用设备树写驱动程序 1.2.1 设备树节点要与platform_driver能匹配 1.2.2 设备树节 ...

  6. i.MX6ULL学习笔记--字符设备驱动程序

    i.MX6ULL学习笔记--字符设备驱动程序 简介 1.驱动的配置过程 1.1设备号 1.2哈希表-chrdevs 1.3哈希表-obj_map->probes 1.4文件操作接口 1.5简单了 ...

  7. 如何学习嵌入式linux[转]

    嵌入式时代已经来临,你还在等什么?   ---循序渐进学习嵌入式开发技术 最近经常有用人单位给 我打来电话,问我这有没有嵌入式Linux方面的开发人员,他们说他们单位急需要懂得在嵌入式linux环境下 ...

  8. Linux与C++11多线程编程(学习笔记)

    多线程编程与资源同步 在Windows下,主线程退出后,子线程也会被关闭; 在Linux下,主线程退出后,系统不会关闭子线程,这样就产生了僵尸进程 3.2.1创建线程 Linux 线程的创建 #inc ...

  9. 通过linux课程我学到了什么作文,linux学习心得怎么写

    Linux是一套免费使用和自由传播的类Unix操作系统,用过的人觉得好用吗?使用它的人有什么心得?下面是小编为大家提供的心得,仅供参考: 之前一直以为Linux和Windows差不多,但是学习了Lin ...

最新文章

  1. getDimension/getDimensionPixelSize/getDimensionPixelOffset()
  2. 面试官:谈谈Redis缓存和MySQL数据一致性问题
  3. linux中mysql回滚重演_DM7 达梦 数据库 数据守护(Data Watch) (1) -- 基本概念
  4. 【MM】基于收货的发票校验Bapi Demo
  5. 探测器反向偏压_科学网—《涨知识啦22》---MSM型光电探测器 - 寇建权的博文
  6. 常见DDoS技术方法和对应防御措施
  7. LeetCode 832. 翻转图像(异或^)
  8. Django REST FRAMEWORK swagger(一)框架详解
  9. java多客户端请求响应_Java实现服务器端对多个客户端请求响应
  10. 中嵌套的页面如何操作父页面_UI设计中签到页面如何设计
  11. java中进制转换_java中的进制转换
  12. php for循环建数据
  13. pythoncad_pythonCAD
  14. 基于51单片机ds18b20智能温控风扇Proteus仿真
  15. 《老路用得上的商学课》56-60学习笔记
  16. 流式传输 android,如何从android流式传输到ffserver
  17. 浅析 微信公共平台消息 开发原理
  18. iPhone6 6p 7 7p屏幕适配,切图准则
  19. 一次 ASAN 找不到 symbolizer 问题的分析与解决
  20. Android 11 : 隐私和安全

热门文章

  1. VB.NET在线升级程序源代码,可以独立使用
  2. Java中遍历文件夹的2种方法
  3. ASCII与unicode的转换
  4. 随想录(设计软件模块的接口)
  5. 源数据怎么排查重复MySQL_面试官:在使用mysql数据库时,遇到重复数据怎么处理?...
  6. 人物结局 归德侯府_影视里的唐伯虎抱得美人归 历史上的他结局又是什么样呢?...
  7. 频率计的交流耦合和直流耦合的区别_直流电源与交流电源是什么?直流与交流之间的区别公司新闻...
  8. 服务器LCD显示面板,DELL服务器2950的错误代码表(前LCD面板)
  9. oracle中的中文排序,Oracle中的中文排序方式
  10. matlab负荷分配程序,利用MATLAB工具箱进行机组负荷优化分配