1、总线模型概述
随着技术的不断进步,系统的拓扑结构也越来越复杂,对热插拔,跨平台移植性的要求也越来越高,2.4内核已经难以满足这些需求。为适应这种形势的需要,从Linux 2.6内核开始提供了全新的设备模型。

假如说现在有一条USB总线,它支持热插拔,支持鼠标、键盘、网卡。当网卡插入时,USB总线需要感知到设备的插入,然后根据设备描述符逐个去匹配和它一样的设备驱动程序,可能先找到鼠标驱动程序,发现处理不了,然后找键盘驱动程序,发现页支持不了,最后找到了网卡驱动程序,可以处理。于是把USB的控制权交给网卡驱动程序,当网卡拔掉时,USB总线同样需要感知到设备的移除,然后通过网卡驱动程序做相应的处理。

2、总线
在Linux系统中,总线的编程模型和字符型,混杂型也类似。在Linux内核中存在一种描述结构来描述总线,还有注册、注销总线设备的函数。

1、总线描述结构
在 Linux 内核中, 总线由 bus_type 结构表示,定义在 <linux/device.h>

struct bus_type {
    const char *name; /*总线名称*/
    int (*match) (struct device *dev, struct
    device_driver *drv); /*驱动与设备的匹配函数*/
    ………
}
它的成员非常多,这里介绍2个重要的成员,第一个成员是总线的名字,第二个成员是对设备的匹配函数,上面提到过,设备插入时,需要匹配对应的设备驱动函数。

int (*match)(struct device * dev, struct device_driver * drv)
当一个新设备或者新驱动被添加到这个总线时,该函数被调用。用于判断指定的驱动程序是否能处理指定的设备。若可以,则返回非零。

2、注册总线
总线的注册使用如下函数
bus_register(struct bus_type *bus)
若成功,新的总线将被添加进系统,并可在/sys/bus 下看到相应的目录。

3、注销总线
总线的注销使用:

void bus_unregister(struct bus_type *bus)

4、创建一条总线

使用上面的代码就可以注册一条总线了:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
 
int my_macth(struct device * dev, struct device_driver * drv)
{
    return 0;
}
 
struct bus_type my_bus_type = {
    .name = "my_bus",
    .match = my_macth,
};
 
static int bus_init(void)
{
    int ret;
    ret = bus_register(&my_bus_type);
    return ret;
}
 
static void bus_exit(void)
{
    bus_unregister(&my_bus_type);
}
 
MODULE_LICENSE("GPL");
module_init(bus_init);
module_exit(bus_exit);

安装这个模块后,使用:

#ls  /sys/bus

命令可以看到我们注册总线的名字

注意,如果代码中没有声明遵循GPL协议,将会出现如下警告,且总线注册失败。

bus: module license 'unspecified' taints kernel.
Disabling lock debugging due to kernel taint
bus: Unknown symbol bus_unregister
bus: Unknown symbol bus_register
insmod: cannot insert 'bus.ko': unknown symbol in module or invalid parameter

3、驱动
总线的设备驱动也分为描述结构,注册和注销这3个主要操作。

1、描述结构
在 Linux内核中, 驱动由 device_driver结构表示。

struct device_driver {
{
    const char *name; /*驱动名称*/
    struct bus_type *bus; /*驱动程序所在的总线*/
    int (*probe) (struct device *dev);
    ………
}
这里列举出3个重要的采用,第一个驱动名称,第二个驱动程序所在的总线,第三个当总线设备和这个驱动匹配时调用probe这个函数

2、注册驱动
驱动的注册使用如下函数
int driver_register(struct device_driver *drv)

3、注销驱动
驱动的注销使用:
void driver_unregister(struct device_driver *drv)

4、创建一个驱动
由于这个驱动是挂载在my_bus下面的,所以在bus.c的代码中需要把这个总线的描述结构导出。

EXPORT_SYMBOL(my_bus_type);

然后根据上面的情况编写驱动程序:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
 
extern struct bus_type my_bus_type;
 
int my_probe(struct device *dev)
{
    printk("driver found the device it can handle!\n");
 
    //实际情况下这里要做初始化
    return 0;
}
struct device_driver my_driver = {
    .name = "my_dev",
    .bus = &my_bus_type,
    .probe = my_probe,
};
 
static int my_driver_init(void)
{
    int ret;
 
    ret = driver_register(&my_driver);
 
    return ret;
}
 
static void my_driver_exit(void)
{
    driver_unregister(&my_driver);
}
MODULE_LICENSE("GPL");
module_init(my_driver_init);
module_exit(my_driver_exit);
编写Makefile后编译

并且把bus和driver都拷贝到开发板中,同时安装,查看总线情况:

#ls /sys/bus/my_bus

在my_bus下有2个目录,devices保存设备,drivers保存驱动。

查看driver目录,如果有my_dev驱动,说明驱动创建成功。

4、设备
同样在Linux设备中仍然由设备描述结构,设备注册,设备注销。

在 Linux内核中, 设备由struct device结构表示。

struct device {
{
    const char *init_name; /*设备的名字*/
    struct bus_type *bus; /*设备所在的总线*/
………
}

设备的注册使用如下函数
int device_register(struct device *dev)
设备的注销使用:
void device_unregister(struct device *dev)

创建一个设备
根据上面的介绍就可以创建一个设备了

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
 
extern struct bus_type my_bus_type;
 
/*描述设备,名字需要和驱动中一样*/
struct device my_device = {
    .init_name = "my_dev",
    .bus = my_bus_type,
};
 
static int my_device_init(void)
{
    int ret;
 
    ret  = device_register(&my_device);
 
    return ret;
}
 
static void my_device_exit(void)
{
    device_unregister(&my_device);
}
 
 
MODULE_LICENSE("GPL");
module_init(my_device_init);
module_exit(my_device_exit);

5、设备与驱动的匹配
编写好驱动程序,创建好设备后,总线怎么把驱动程序和设备对应在一起呢?其实是通过match来实现的。当然不同的总线匹配规则也不一样,比如说USB总线,在匹配驱动程序时会依次比较设备和驱动的ID,如果相同就匹配成功。我们这里采用名字匹配的方法,比如设备的名字和驱动的名字一样,则认为匹配成功,调用这个驱动的prob函数。下面开始实现总线中的match函数。

int my_macth(struct device * dev, struct device_driver * drv)
{
    return !strncmp(dev->init_name, drv->name,strlen(drv->name));
}

最后,bus的代码如下:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
 
int my_macth(struct device * dev, struct device_driver * drv)
{
    return !strncmp(dev->init_name, drv->name,strlen(drv->name));
}
 
struct bus_type my_bus_type = {
    .name = "my_bus",
    .match = my_macth,
};
 
//输出my_bus_type供别的驱动使用
EXPORT_SYMBOL(my_bus_type);
 
static int bus_init(void)
{
    int ret;
    ret = bus_register(&my_bus_type);
    return ret;
}
 
static void bus_exit(void)
{
    bus_unregister(&my_bus_type);
}
 
MODULE_LICENSE("GPL");
module_init(bus_init);
module_exit(bus_exit);

把bus,driver,device分别在开发板上安装,发现在安装device的时候内核发生了异常,提示如下:

Unable to handle kernel NULL pointer dereference at virtual address 00000000
pgd = c39c4000
[00000000] *pgd=339bf031, *pte=00000000, *ppte=00000000
Internal error: Oops: 17 [#1]
last sysfs file: /sys/devices/platform/soc-audio/sound/card0/mixer/dev
Modules linked in: device(+) driver bus
CPU: 0    Not tainted  (2.6.32.2-FriendlyARM #17)
PC is at strncmp+0x14/0x68
LR is at my_macth+0x2c/0x38 [bus]
pc : [<c014f6dc>]    lr : [<bf000064>]    psr: 20000013
sp : c39a7e38  ip : c39a7e48  fp : c39a7e44
r10: 00000000  r9 : 00000000  r8 : bf00c070
r7 : c0187330  r6 : bf00c068  r5 : bf00c068  r4 : bf0060a4
r3 : 00000000  r2 : 00000006  r1 : bf0060a4  r0 : 00000000
Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment user
Control: c000717f  Table: 339c4000  DAC: 00000015
Process insmod (pid: 682, stack limit = 0xc39a6270)
Stack: (0xc39a7e38 to 0xc39a8000)
7e20:                                                       c39a7e5c c39a7e48
7e40: bf000064 c014f6d8 bf0060ac bf00c068 c39a7e74 c39a7e60 c0187364 bf000048
7e60: 00000000 c39a7e78 c39a7e9c c39a7e78 c0186680 c0187340 c38feec8 c394f574
7e80: bf00c068 bf00c09c bf00c108 00000000 c39a7eb4 c39a7ea0 c0187408 c0186624
7ea0: c04ce058 bf00c068 c39a7ec4 c39a7eb8 c0186474 c01873b4 c39a7f0c c39a7ec8
7ec0: c0184cb0 c0186458 00000000 c034b8f4 c39a7ef4 c39a7ee0 c014b490 c014c44c
7ee0: bf00c068 bf00c068 00191e54 bf00c108 c39a6000 c04a3980 00000000 bf00c01c
7f00: c39a7f24 c39a7f10 c0184ed8 c01849a8 00000969 00191e54 c39a7f34 c39a7f28
7f20: bf00c030 c0184ecc c39a7f7c c39a7f38 c003032c bf00c02c 00000000 00000000
7f40: 00000000 00000969 00191e54 bf00c108 00000000 00000969 00191e54 bf00c108
7f60: 00000000 c00310c8 c39a6000 00000000 c39a7fa4 c39a7f80 c0070344 c0030300
7f80: c009b6b0 c009b5a0 00000000 00000000 bead6eb8 00000080 00000000 c39a7fa8
7fa0: c0030f20 c0070284 00000000 00000000 001aafd8 00000969 00191e54 00000000
7fc0: 00000000 00000000 bead6eb8 00000080 bead6eb4 bead6eb8 00000001 bead6eb4
7fe0: 00000069 bead6b7c 00020d48 000094b4 60000010 001aafd8 3055b031 3055b431
Backtrace: 
[<c014f6c8>] (strncmp+0x0/0x68) from [<bf000064>] (my_macth+0x2c/0x38 [bus])
[<bf000038>] (my_macth+0x0/0x38 [bus]) from [<c0187364>] (__device_attach+0x34/0x4c)
 r5:bf00c068 r4:bf0060ac
[<c0187330>] (__device_attach+0x0/0x4c) from [<c0186680>] (bus_for_each_drv+0x6c/0x98)
 r5:c39a7e78 r4:00000000
[<c0186614>] (bus_for_each_drv+0x0/0x98) from [<c0187408>] (device_attach+0x64/0x7c)
 r7:00000000 r6:bf00c108 r5:bf00c09c r4:bf00c068
[<c01873a4>] (device_attach+0x0/0x7c) from [<c0186474>] (bus_probe_device+0x2c/0x4c)
 r5:bf00c068 r4:c04ce058
[<c0186448>] (bus_probe_device+0x0/0x4c) from [<c0184cb0>] (device_add+0x318/0x524)
[<c0184998>] (device_add+0x0/0x524) from [<c0184ed8>] (device_register+0x1c/0x20)
[<c0184ebc>] (device_register+0x0/0x20) from [<bf00c030>] (my_device_init+0x14/0x1c [device])
 r5:00191e54 r4:00000969
[<bf00c01c>] (my_device_init+0x0/0x1c [device]) from [<c003032c>] (do_one_initcall+0x3c/0x1c8)
[<c00302f0>] (do_one_initcall+0x0/0x1c8) from [<c0070344>] (sys_init_module+0xd0/0x204)
[<c0070274>] (sys_init_module+0x0/0x204) from [<c0030f20>] (ret_fast_syscall+0x0/0x28)
 r7:00000080 r6:bead6eb8 r5:00000000 r4:00000000
Code: e92dd800 e24cb004 e3520000 0a00000e (e5d0c000) 
---[ end trace ed7d32a3ca1538b0 ]---
Segmentation fault

我们要学会分析这段提示,这里有一段非常重要的函数调用回溯表:

Backtrace: 
[<c014f6c8>] (strncmp+0x0/0x68) from [<bf000064>] (my_macth+0x2c/0x38 [bus])
[<bf000038>] (my_macth+0x0/0x38 [bus]) from [<c0187364>] (__device_attach+0x34/0x4c)
 r5:bf00c068 r4:bf0060ac
[<c0187330>] (__device_attach+0x0/0x4c) from [<c0186680>] (bus_for_each_drv+0x6c/0x98)
 r5:c39a7e78 r4:00000000
[<c0186614>] (bus_for_each_drv+0x0/0x98) from [<c0187408>] (device_attach+0x64/0x7c)
 r7:00000000 r6:bf00c108 r5:bf00c09c r4:bf00c068
[<c01873a4>] (device_attach+0x0/0x7c) from [<c0186474>] (bus_probe_device+0x2c/0x4c)
 r5:bf00c068 r4:c04ce058
[<c0186448>] (bus_probe_device+0x0/0x4c) from [<c0184cb0>] (device_add+0x318/0x524)
[<c0184998>] (device_add+0x0/0x524) from [<c0184ed8>] (device_register+0x1c/0x20)
[<c0184ebc>] (device_register+0x0/0x20) from [<bf00c030>] (my_device_init+0x14/0x1c [device])
 r5:00191e54 r4:00000969
[<bf00c01c>] (my_device_init+0x0/0x1c [device]) from [<c003032c>] (do_one_initcall+0x3c/0x1c8)
可以看到问题出在strncmp函数,然后结合第一句话,内核不能处理空指针,所以strncmp比较函数中一定有一个是空指针。原因在于设备的注册函数中,在设备注册过程的时候会把init_name赋值给另外一个参数,然后把它清空。所以应该使用别的参数比较:

int my_macth(struct device * dev, struct device_driver * drv)
{
    return !strncmp(dev->kobj.name, drv->name,strlen(drv->name));
}
    return !strncmp(dev->kobj.name, drv->name,strlen(drv->name));
}

重新编译后,再安装,最后安装设备的时候会打印出一条信息,到这里就完成了总线的驱动了!

如果说先挂载设备,再安装驱动,会发生什么情况呢?设备能够正常工作吗?我们试一试,

结果发现同样打印出了信息。因为在安装驱动的时候,驱动同样会去匹配当前总线上的设备,如果匹配成功,则开始处理这里设备!
————————————————
版权声明:本文为CSDN博主「dayL_W」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u013181595/article/details/74111289

linux 设备 总线 驱动 模型相关推荐

  1. Linux 设备总线驱动模型(转载)

    尽管LDD3中说对多数程序员掌握设备驱动模型不是必要的,但对于嵌入式Linux的底层程序员而言,对设备驱动模型的学习非常重要.     Linux设备模型的目的:为内核建立一个统一的设备模型,从而又一 ...

  2. Linux总线、设备、驱动模型

    一.Linux总线设备驱动模型框架 从Linux2.6开始Linux加入了一套驱动管理和注册机制-platform平台总线驱动模型.platform平台总线是一条虚拟总线,platform_devic ...

  3. linux驱动:设备-总线-驱动(以TI+DM8127中GPIO为例)

    一:说明:这次学习设备-总线-驱动是以TI+DM8127的GPIO为例 1.GPIO资源注册到omap_hwmod链表中 2.初始化GPIO 3.将GPIO注册到plarform层 4.将GPIO注册 ...

  4. Linux设备与驱动学习之----什么是设备

    [ 声明:版权所有,欢迎转载,转载请注明出处,请勿用于商业用途] [ 声明:本文属于作者个人理解,如有错误,欢迎大家指正] 在学习Linux设备驱动的过程中我们用到也是看到最多的就是设备和驱动了,接下 ...

  5. Linux 总线、设备、驱动模型的探究

    学习交流加 个人qq:1126137994 个人微信:liu1126137994 学习交流资源分享qq群:962535112 之前一直做项目,做项目的过程虽然也学习到了不少知识,但是,一直没有好好研究 ...

  6. Linux平台总线驱动设备模型

    platform总线是一种虚拟的总线,相应的设备则为platform_device,而驱动则为platform_driver.Linux 2.6的设备驱动模型中,把I2C.RTC.LCD等都归纳为pl ...

  7. 【嵌入式环境下linux内核及驱动学习笔记-(16)linux总线、设备、驱动模型之input框架】

    目录 1.Linux内核输入子系统概念导入 1.1 输入设备工作机制 1.2 运行框架 1.3 分层思想 2.驱动开发步骤 2.1 在init()或probe()函数中 2.2 在exit()或rem ...

  8. Linux设备驱动---OMAP3630 Linux I2C总线驱动分析(1)

    原文地址:http://blog.csdn.net/kellycan/article/details/6394737 1 Linux I2C驱动架构 Linux下I2C驱动的架构图如下: 图1.1 L ...

  9. linux设备和驱动加载的先后顺序

    点击打开链接 Linux驱动先注册总线,总线上可以先挂device,也可以先挂driver,那么究竟怎么控制先后的顺序呢. Linux系统使用两种方式去加载系统中的模块:动态和静态. 静态加载:将所有 ...

  10. 宋宝华:Linux设备与驱动的手动解绑与手动绑定

    众所周知,Linux靠设备与驱动之间的match,来完成设备与驱动的bind,从而触发驱动的probe()成员函数被执行.每个bus都有相应的match方法,完成match的总的入口函数是: stat ...

最新文章

  1. Logstash5.4.1解析日志报错处理
  2. AWS — AWS Snow 系列
  3. SpringBoot2.x 整合websocket 消息推送,单独发送信息,群发信息
  4. 开课吧Java课程之详解文件输出流FileInputStream
  5. 【ACM】nyoj_2_括号配对问题_201308091548
  6. JAVA笔记整理(五),JAVA中的继承
  7. web开发路径问题解决
  8. python画代码流程图_python如何画流程图
  9. office 安装失败原因
  10. 【新技能get】让App像Web一样发布新版本
  11. Just For Fun(乐者为王)——Linux创始人
  12. 山特UPS不间断电源的主要技术参数
  13. OOALV中字段设置ALV中下拉列表列的实现
  14. Dashed hopes 破灭的希望,新兴国家的出路在何方?经济学人精读
  15. 在前端开发中,页面渲染指什么
  16. 8. 设计模式 外观模式 一键电影模式
  17. ChatGPT 大智近妖,从宇宙人生到手搓光刻机,从哄女朋友到写年终总结我们聊得非常开心,反而让人越来越忧心...
  18. FreeCAD源码分析:FreeCADGui模块
  19. 解决Ubuntu 8.04下8600M GT显卡无法使用的问题
  20. 性格测试c语言程序,性格测试题目及答案

热门文章

  1. SQL Sever — 附加【如何导入外部文件数据库】
  2. MTK平台调试加密芯片ATSHA204A
  3. 十八.描述符(__get__,__set__,__delete__)
  4. selenium报错TypeError: 'FirefoxWebElement' object is not iterable
  5. 组合数学+gcd BZOJ3505 [Cqoi2014]数三角形
  6. ASP.Net Web API 的参数绑定[翻译]
  7. PAT A 1065. A+B and C (64bit) (20)
  8. [九度][何海涛] 二叉树中和为某一值的路径
  9. 一个button同时执行多个有返回值的函数的解决方法(return false; or return true;)...
  10. 华为VRRP+VLAN+DHCP配置实例