zoukankan      html  css  js  c++  java
  • linux设备驱动归纳总结(八):2.match.probe.remove

    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("<kernel> 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函数

    <kernel> 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

    <kernel> 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

    <script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>
    阅读(62) | 评论(0) | 转发(0) |
给主人留下些什么吧!

~~

评论热议
查看全文
  • 相关阅读:
    一步一步使用Ext JS MVC与Asp.Net MVC 3开发简单的CMS后台管理系统之用户管理(1)
    一步一步使用Ext JS MVC与Asp.Net MVC 3开发简单的CMS后台管理系统之创建Viewport(2)
    一步一步使用Ext JS MVC与Asp.Net MVC 3开发简单的CMS后台管理系统之用户管理(2)
    一步一步使用Ext JS MVC与Asp.Net MVC 3开发简单的CMS后台管理系统之完成登录功能
    一步一步使用Ext JS MVC与Asp.Net MVC 3开发简单的CMS后台管理系统之登录窗口调试
    一步一步使用Ext JS MVC与Asp.Net MVC 3开发简单的CMS后台管理系统之创建Viewport(1)
    一步一步使用Ext JS MVC与Asp.Net MVC 3开发简单的CMS后台管理系统之创建输出验证码图片的控制器
    一步一步使用Ext JS MVC与Asp.Net MVC 3开发简单的CMS后台管理系统之调整首页显示
    一步一步使用Ext JS MVC与Asp.Net MVC 3开发简单的CMS后台管理系统之登录窗口
    一步一步使用Ext JS MVC与Asp.Net MVC 3开发简单的CMS后台管理系统之用户管理(3)
  • 原文地址:https://www.cnblogs.com/yangykaifa/p/7147124.html
  • Copyright © 2011-2022 走看看