zoukankan      html  css  js  c++  java
  • 【Linux开发】linux设备驱动归纳总结(八):2.总线、设备和驱动的关系

    linux设备驱动归纳总结(八):2.总线、设备和驱动的关系


    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

    上一节介绍了总线、设备和驱动函数的注册,这节着重介绍它们三者的关系,和上一节一样,我模拟一条usb总线,一个usb鼠标设备和一个usb鼠标驱动函数,当然,只是名字是usb,里面并没有实质的操作,只是通过这样来介绍一下三者之间的关系。。

    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


    一、总线、设备和驱动函数在/sys/中的框架


    首先要写三个函数,bus.cdevice.cdriver.c。这几个函数其实就是上一节函数的精简版,去掉属性文件的创建,仅仅保留创建和注销操作。

    第一个函数是bus.c,加载模块会创建了一条名叫usb的总线,总线目录放在/sys/bus/目录下:

    /*8th_devModule_2/1st/bus.c*/

    6 struct bus_type usb_bus = {

    7 .name = "usb",         //注册成功后将在/sys/bus目录下看到目录usb

    8 };

    9

    10 static int __init usb_bus_init(void)

    11 {

    12 int ret;

    13 /*总线注册,必须检测返回值*/

    14 ret = bus_register(&usb_bus);

    15 if(ret){

    16 printk("bus register failed! ");

    17 return ret;

    18 }

    19

    20 printk("usb bus init ");

    21 return 0;

    22 }

    23

    24 static void __exit usb_bus_exit(void)

    25 {

    26 bus_unregister(&usb_bus);

    27 printk("usb bus bye! ");

    28 }

    第二个函数是device.c,加载模块会创建目录/sys/device/usb_device来管理这个usb设备。

    由于该设备指定了所属的总线是usb_bus,所有会在/sys/bus/usb/device目录下创建一了指向usb_device的软连接。

    同时,在卸载模块时,usb_deivce被删除,内核自动调用release函数,现实当中release函数应该做一些卸载设备的相关操作,但是我的usb设备是我虚拟出来的,所以release函数只是打印了一句话。

    /*8th_devModule_2/1st/device.c*/

    5 extern struct bus_type usb_bus;

    6

    7 void usb_dev_release(struct device *dev) //卸载函数没有干具体的事情

    8 {

    9 printk(" release ");

    10 }

    11

    12 struct device usb_device = {

    13 .bus_id = "usb_device",

    14 .bus = &usb_bus,                   //指定该设备的总线,/sys/bus/usb

    15 .release = usb_dev_release, //必须要都有release函数,不然卸载时会出错

    16 };

    17

    18 static int __init usb_device_init(void)

    19 {

    20 int ret;

    21 /*设备注册,注册成功后在/sys/device目录下创建目录usb_device并在指定总线

    22 * usb_bus的目录/sys/bus/usb/device创建/sys/device/usb_device的软连接*/

    23 ret = device_register(&usb_device);

    24 if(ret){

    25 printk("device register failed! ");

    26 return ret;

    27 }

    28

    29 printk("usb device init ");

    30 return 0;

    31 }

    32

    33 static void __exit usb_device_exit(void)

    34 {

    35 device_unregister(&usb_device);

    36 printk("usb device bye! ");

    37 }

    第三个函数是driver.c,加载模块后会在指定的总线目录的driver目录,即/sys/bus/usb/driver目录下创建一个名叫usb_driver的目录来管理这个驱动函数。

    /*8th_devModule_2/1st/driver.c*/

    6 extern struct bus_type usb_bus;

    7

    8 struct device_driver usb_driver = {

    9 .name = "usb_driver", ///sys/中的驱动目录名字

    10 .bus = &usb_bus,      //必须指定驱动函数所属总线,不然不能注册。

    11 };

    12

    13 static int __init usb_driver_init(void)

    14 {

    15 int ret;

    16 /*驱动注册,注册成功后在/sys/bus/usb/driver目录下创建目录usb_driver*/

    17 ret = driver_register(&usb_driver);

    18 if(ret){

    19 printk("driver register failed! ");

    20 return ret;

    21 }

    22 printk("usb driver init ");

    23 return 0;

    24 }

    25

    26 static void __exit usb_driver_exit(void)

    27 {

    28 driver_unregister(&usb_driver);

    29 printk("usb driver bye! ");

    30 }

    接下来看看效果,因为设备和驱动的都指定了所属总线,所以必须先加载总线的模块。同样的,在卸载总线的模块前,必须先把设备和驱动的模块先卸载。

    [root: 1st]# insmod bus.ko //先加载bus.ko

    usb bus init

    [root: 1st]# ls /sys/bus/ //sys/bus目录下多了一个usb目录

    platform scsi usb

    [root: 1st]# insmod device.ko //再加载device.ko

    usb device init

    [root: 1st]# ls /sys/devices/ //sys/device目录下多了一个usb_device目录

    platform system usb_device virtual

    [root: 1st]# ls -l /sys/bus/usb/devices/ //同时将该目录软连接到指定的总线目录下

    lrwxrwxrwx 1 root root 0 Oct 27 13:28 usb_device -> ../../../devices/usb_device

    [root: 1st]# insmod driver.ko //加载driver.ko

    usb driver init

    [root: 1st]# ls /sys/bus/usb/drivers //在指定总线的driver目录下多了一个usb_driver目录

    usb_driver //但它和设备的目录不一样,它并不是软连接。

    [root: 1st]# lsmod //查看一下当前加载的模块

    driver 1256 0 - Live 0xbf00c000

    device 1560 0 - Live 0xbf006000

    bus 1336 2 driver,device, Live 0xbf000000 //模块的引用计数,bus模块被devicedriver引用

    [root: 1st]# rmmod bus //如果你要卸载bus模块,它会提示出错,要先卸载driverdevice

    rmmod: remove 'bus': Resource temporarily unavailable

    [root: 1st]# rmmod driver

    usb driver bye!

    [root: 1st]# rmmod device //卸载device时,内核自动调用device结构体中指定的release函数

    release

    usb device bye!

    [root: 1st]# lsmod //bus的引用计数为0,可以卸载bus

    bus 1336 0 - Live 0xbf012000

    [root: 1st]# rmmod bus

    usb bus bye!

    最后来个图:


    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


    二、配对函数(match)、探测函数(probe)和卸载函数(remove)


    现在讲一下三个函数:


    第一个是配对函数(match),它是总线结构体bus_type的其中一个成员:

    57 int (*match)(struct device *dev, struct device_driver *drv);

    当总线上添加了新设备或者新驱动函数的时候,内核会调用一次或者多次这个函数。

    举例,如果我现在添加了一个新的驱动函数,内核就会调用所属总线的match函数,配对总线上所有的设备,如果驱动能够处理其中一个设备,函数返回0,告诉内核配对成功。

    一般的,match函数是判断设备的结构体成员device->bus_id和驱动函数的结构体成员device_driver->name是否一致,如果一致,那就表明配对成功

    所以,bus.c修改如下,贴上修改的代码:

    /*8th_devModule_2/2nd/bus.c*/

    6 int usb_bus_match(struct device *dev, struct device_driver *drv)

    7 {

    8 if(!strcmp(dev->bus_id, drv->name)){

    9 printk("match success "); //为了配对成功,设备的bus_id和驱动的name我都更改为

    10 return 1;                             //usb_mouse,详细的可以查看device.cdriver.c

    11 }else{

    12 printk("match failed ");

    13 return 0;

    14 }

    15 }

    16

    17 struct bus_type usb_bus = {

    18 .name = "usb",                    //注册成功后将在/sys/bus目录下看到目录usb

    19 .match = usb_bus_match,

    20 };


    第二个是探测函数(probe),它是驱动函数结构体中的一个成员:

    129 int (*probe) (struct device *dev);

    当配对(match)成功后,内核就会调用指定驱动中的probe函数来查询设备能否被该驱动操作,如果可以,驱动就会对该设备进行相应的操作,如初始化。所以说,真正的驱动函数入口是在probe函数中

    所以,driver.c修改如下:

    /*8th_devModule_2/2nd/driver.c*/

    8 void init_mouse(void)

    9 {

    10 printk("init usb mouse ");

    11 }

    12

    13 int usb_driver_probe(struct device *dev)

    14 {//查询特定设备是否存在,以及是否能够才操作该设备,然后再进行设备操作。

    15 //check_mouse(); //自己假设一下检查设备

    16 init_mouse(); //usb鼠标驱动的真正入口

    17 return 0;

    18 }

    。。。。。

    26 struct device_driver usb_driver = {

    27 .name = "usb_mouse", ///sys/中的驱动目录名字,为了配对成功,修改为usb_mouse

    28 .bus = &usb_bus, //必须指定驱动函数所属总线,不然不能注册。

    29 .probe = usb_driver_probe,

    30 。。。。。

    31 };


    第三个是卸载函数(remove),它是驱动函数结构体中的一个成员:

    130 int (*remove) (struct device *dev);

    当该驱动函数或者驱动函数正在操作的设备被移除时,内核会调用驱动函数中的remove函数调用,进行一些设备卸载相应的操作。

    所以,driver.c修改如下:

    /*8th_devModule_2/2nd/driver.c*/

    20 int usb_driver_remove(struct device *dev)

    21 {

    22 printk("remove mouse driver ");

    23 return 0;

    24 }

    25

    26 struct device_driver usb_driver = {

    27 .name = "usb_mouse", ///sys/中的驱动目录名字

    28 .bus = &usb_bus, //必须指定驱动函数所属总线,不然不能注册。

    29 .probe = usb_driver_probe,

    30 .remove = usb_driver_remove,

    31 };

    接下来就要验证一下了。当然,我的函数里面并没有真正的硬件操作,仅仅是打印出一句话:

    [root: 2nd]# insmod bus.ko //必须先加载总线模块

    usb bus init

    [root: 2nd]# insmod device.ko

    usb device init

    [root: 2nd]# insmod driver.ko

    match success //当加载了设备和驱动的模块后,内核调用总线的配对函数

    match success //并且配对成功。

    init usb mouse //配对成功后内核调用探测函数probe

    usb driver init

    [root: 2nd]# rmmod device

    remove mouse driver //当设备卸载时,内核调用驱动函数中的remove

    release //同时也会调用设备中的release函数调用

    usb device bye!

    [root: 2nd]# rmmod driver

    usb driver bye!

    又到了举例时间,程序员最喜欢就是男女关系,那就以男女关系举例:


    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


    三、多个设备和驱动之间的配对


    上面讲的内容都是一对一的配对,但是如果系统中有多个配对成功,内核会如何处理呢?


    1、多个设备对应一个驱动:


    下面要讲的情况是,如果多个设备与内核中的一个驱动函数配对成功时,内核会进行怎么样的操作,先看实例。

    为了能够让多个设备配对成功,我将bus.c的配对条件修改了一下:

    /*8th_devModule_2/3th/bus.c */

    6 int usb_bus_match(struct device *dev, struct device_driver *drv)

    7 { //仅仅配对名字的前9个字母是否相同

    8 if(!strncmp(dev->bus_id, drv->name, 9)){

    9 printk("match success ");

    10 return 1;

    11 }else{

    12 printk("match failed ");

    13 return 0;

    14 }

    15 }

    同时在device.c的基础上拷贝了device1.cdevice2.c,三个程序都差不多,可以自己看看。接下来直接看效果:

    [root: /]# cd /review_driver/8th_devModule/8th_devModule_2/3th

    [root: 3th]# insmod bus.ko //先加载总线

    usb bus init

    [root: 3th]# insmod driver.ko //再加载驱动

    usb driver init

    [root: 3th]# insmod device.ko //当加载device.ko时,配对成功

    match success

    init usb mouse //内核调用驱动中的probe

    usb device init

    [root: 3th]# insmod device1.ko //再加载device1.ko,也配对成功

    match success

    init usb mouse //内核有调用驱动中的probe

    usb device1 init

    [root: 3th]# insmod device2.ko //加载device2.ko,配对不成功

    match failed

    usb device2 init

    上面的验证表明,一个驱动可以对应多个设备。在联想起我举得男人女人——一个男人可以配对多个女人,哈哈。


    2、一个设备对应多个驱动


    这个例子中我将driver.c拷贝多了一个driver1.c,两个程序基本相同,都能配对成功,但看看效果:

    [root: 3th]# insmod bus.ko //先加载总线

    usb bus init

    [root: 3th]# insmod device.ko //再加载设备

    usb device init

    [root: 3th]# insmod driver.ko //加载driver.ko

    match success //配对成功

    match success

    init usb mouse //并且调用了probe

    usb driver init

    [root: 3th]# insmod driver1.ko //再加载driver1.ko

    match success //因为名字的前9个字母一样,所以也会配对成功

    usb driver1 init //但不会调用probe,因为已经有一个驱动跟该设备配对了。

    上面的验证表明,一个设备只能对应一个驱动


    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


    四、总结


    这节内容主要介绍了总线和驱动中的几个方法和总线、设备和驱动函数三者之间的关系。


    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

    源代码: 8th_devModule_2.rar   

    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

    写到现在,都差不多踏入新年了,不知道还有没有人在看博客,预祝一下新年快乐。总结还没写完,年前任务没完成。过年期间继续复习。

    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

  • 相关阅读:
    黑马乐优商城项目总结
    SpringDataJpa学习(3)——SpringDataJpa的多表映射和动态查询
    SpringDataJpa学习(2)——SpringDataJpa的单表使用
    SpringDataJpa学习(1)——Jpa学习
    个人博客06
    个人博客05
    个人博客04
    典型用户和用户场景描述
    个人博客03
    个人博客02
  • 原文地址:https://www.cnblogs.com/huty/p/8518559.html
Copyright © 2011-2022 走看看