8.总线设备驱动模型

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了8.总线设备驱动模型相关的知识,希望对你有一定的参考价值。

总线设备驱动模型

技术分享

技术分享

总线:创建一条总线,跟我们前面的按键一样,首先是描述总线结构,接着是注册总线,注销总线。总线设备,例如usb总线,上面会有很多类型的usb的驱动,例如鼠标、键盘.....等,当我们把之一的usb插上的时候,usb总线会把每个驱动遍历一遍,找到相应的驱动程序执行。

技术分享

技术分享

技术分享

技术分享

接下来用bus.c创建一条总线。

 

#include <linux/init.h>

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/device.h>

int my_match(struct device *dv, struct device_driver *drv)

{

    return 0;

}

 

struct bus_type my_bus_type=

{

    .name="mybus",

    .match=my_match,

};

 

int mybus_init()

{

    int ret;

    ret=bus_register(&my_bus_type);

    return ret;

}

void mybus_exit()

{

    bus_unregister(&my_bus_type);

}

module_init(mybus_init);

module_exit(mybus_exit);

Make通过,拷贝到开发板运行:出现这错误:

技术分享

错误的提示是未定义的bus总线相关的未定义符号。主要的要用是,我们的驱动程序没有遵循GPL协议,加上MODULE_LICENSE("GPL");就可以了。

技术分享

下面的mybus总线就是我们刚新建的:

技术分享

接下来是总线驱动的实现:

技术分享

上面的prode函数,当我们有设备加到总线的时候,当设备与总线的某个借口相匹配的时候,系统就会调用prode函数。对我的设备进行相应的初始化。

技术分享

接下来在我们上面的总线挂接个驱动。

我们的driver.c的代码:

#include <linux/device.h>

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/init.h>

MODULE_LICENSE("GPL");

extern struct bus_type my_bus_type;

 

int my_probe(struct device *dev)

{

    printk("<0> driver found the device it can handle!\n");

    return 0;

    //如果是实际应用的驱动,这里会做很多的硬件初始化操作。

}

 

 

struct device_driver my_driver=

{

    .name="my_dev",

    .bus = &my_bus_type,//驱动是属于那一条总线的。来自外部的。所以总线的代码

//要有EXPORT_SYMBOL符号导出标志

    .probe=my_probe,

};

 

int mydriver_init()

{

    int ret;

    ret=driver_register(&my_driver);

    return ret;

}

 

void mydriver_exit()

{

    driver_unregister(&my_driver);

}

module_init(mydriver_init);

module_exit(mydriver_exit);

由于驱动程序用到bus.c里的my_bus_type。所以在该结构的下面导出这个符号:EXPORT_SYMBOL(my_bus_type);//符号输出。最后重新make一遍。把生成的bus.ko和driver.ko拷贝到开发板执行的结果如下:

 

[[email protected]]# insmod bus.ko

[[email protected]]# insmod driver.ko

[[email protected]]# cd /sys/bus/

[[email protected]]# ls

ac97 i2c mybus sdio usb

event_source mdio_bus platform serio usb-serial

hid mmc scsi spi

[[email protected]]# cd mybus/

[[email protected]]# ls

devices drivers_autoprobe uevent

drivers drivers_probe

[[email protected]]# cd drivers/

[[email protected]]# ls

my_dev

[[email protected]]#

上面的目录/sys/bus/存的是系统总线的各类接口,我们看到了我们创建的mybus总线,进去,打开驱动drivers的目录,里面有我们创建的驱动my_dev。这说明了我们在总线上成功地挂载了我们的驱动。

接下来在我们上面的总线挂接个设备。

技术分享

技术分享

Device.c的代码:

#include <linux/module.h>

#include <linux/device.h>

#include <linux/kernel.h>

#include <linux/init.h>

 

MODULE_LICENSE("GPL");

extern struct bus_type my_bus_type;

struct device my_dev=

{

    .init_name = "my_dev",//与驱动一致

    .bus = &my_bus_type,

};

int my_device_init()

{

    int ret;

    ret=device_register(&my_dev);

    return ret;

 

}

void my_device_exit()

{

    device_unregister(&my_dev);

}

 

 

module_init(my_device_init);

module_exit(my_device_exit);

 

对于我们的bus.c也需要做相应的修改。就是驱动程序和设备文件要对应上才能实现操作。把my_match函数修改如下:

int my_match(struct device *dev, struct device_driver *drv)

{

return !strncmp(dev->init_name,drv->name,strlen(drv->name));

//驱动程序和设备的匹配程序

}

最后就是在我们的Makefile里面添加device.o。后执行Make:

[[email protected] bus]# make

make -C /home/samba/linux-ok6410 M=/home/module/bus modules ARCH=arm CROSS_COMPILE=arm-linux-

make[1]: Entering directory `/home/samba/linux-ok6410‘

CC [M] /home/module/bus/device.o

/home/module/bus/device.c:14: warning: function declaration isn‘t a prototype

/home/module/bus/device.c:21: warning: function declaration isn‘t a prototype

Building modules, stage 2.

MODPOST 3 modules

CC /home/module/bus/bus.mod.o

LD [M] /home/module/bus/bus.ko

CC /home/module/bus/device.mod.o

LD [M] /home/module/bus/device.ko

CC /home/module/bus/driver.mod.o

LD [M] /home/module/bus/driver.ko

make[1]: Leaving directory `/home/samba/linux-ok6410‘

完成之后,拷贝bus.ko driver.ko.device.ko到开发板,测试。

 

[[email protected]]# insmod bus.ko

[[email protected]]# insmod driver.ko

[[email protected]]# insmod device.ko

Unable to handle kernel NULL pointer dereference at virtual address 00000000

pgd = cbcdc000

[0te=00000000

Internal error: Oop

Modules linked in: device(+) driver bus

CPU: 0 Tainted: G W (3.0.1 #439)

PC is at strncmp+0x14/0x68

LR is at my_match+0x2c/0x38 [bus]

pc : [<c01f9348>] lr : [<bf000064>] psr: 20000013

sp : cbd83e10 ip : cbd83e20 fp : cbd83e1c

r10: 00000000 r9 : bf008098 r8 : c07b121c

r7 : c0244e48 r6 : bf008090 r5 : bf008090 r4 : bf0040e4

r3 : 00000000 r2 : 00000006 r1 : bf0040e4 r0 : 00000000

Flags: nzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment user

Control: 00c5387d Table: 5bcdc008 DAC: 00000015

Process insmod (pid: 128, stack limit = 0xcbd82268)

Stack: (0xcbd83e10 to 0xcbd84000)

3e00: cbd83e34 cbd83e20 bf000064 c01f9340

3e20: bf0040ec bf008090 cbd83e4c cbd83e38 c0244e78 bf000044 00000000 cbd83e50

3e40: cbd83e74 cbd83e50 c024405c c0244e54 cbc67b88 cc716874 bf008090 bf008090

3e60: bf0080c4 00000000 cbd83e94 cbd83e78 c0244f44 c0244000 00000000 bf008090

3e80: c0706008 00000000 cbd83ea4 cbd83e98 c0243e20 c0244ec8 cbd83efc cbd83ea8

3ea0: c0242994 c0243e00 00000000 00000000 00000001 00000000 cbd83f0c cbd83ec8

3ec0: cbd83ee4 cbd83ed0 c01f48f8 d1a3548d bf008090 bf008090 00000000 bf008138

3ee0: 00000000 cbd82000 bf00801c 00000000 cbd83f14 cbd83f00 c0242c4c c024257c

3f00: c07463c0 00000000 cbd83f24 cbd83f18 bf008030 c0242c3c cbd83f7c cbd83f28

3f20: c00343c8 bf008028 cbd83f64 cbd83f38 c0073e24 00000000 00000000 00000000

3f40: 00000000 000088a4 000d5bf9 bf008138 00000000 000088a4 000d5bf9 bf008138

3f60: 00000000 c0034ce8 cbd82000 00000000 cbd83fa4 cbd83f80 c0085960 c0034398

3f80: c00e8738 c00e8610 402704a8 000dfcf8 00000000 00000080 00000000 cbd83fa8

3fa0: c0034b40 c00858e0 402704a8 000dfcf8 01327038 000088a4 000d5bf9 ffff5f01

3fc0: 402704a8 000dfcf8 00000000 00000080 00000069 00000001 becf2e84 becf2e88

3fe0: becf2e88 becf2b34 00021cfc 40331d74 60000010 01327038 5fffe821 5fffec21

[<c01f9348>] (strncmp+0x14/0x68) from [<bf000064>] (my_match+0x2c/0x38 [bus])

[<bf000064>] (my_match+0x2c/0x38 [bus]) from [<c0244e78>] (__device_attach+0x30)

[<c0244e78>] (__device_attach+0x30/0x48) from [<c024405c>] (bus_for_each_drv+0x)

[<c024405c>] (bus_for_each_drv+0x68/0x94) from [<c0244f44>] (device_attach+0x88)

[<c0244f44>] (device_attach+0x88/0xa0) from [<c0243e20>] (bus_probe_device+0x2c)

[<c0243e20>] (bus_probe_device+0x2c/0x4c) from [<c0242994>] (device_add+0x424/0)

[<c0242994>] (device_add+0x424/0x6c0) from [<c0242c4c>] (device_register+0x1c/0)

[<c0242c4c>] (device_register+0x1c/0x20) from [<bf008030>] (init_module+0x14/0x)

[<bf008030>] (init_module+0x14/0x1c [device]) from [<c00343c8>] (do_one_initcal)

[<c00343c8>] (do_one_initcall+0x3c/0x188) from [<c0085960>] (sys_init_module+0x)

[<c0085960>] (sys_init_module+0x8c/0x1a4) from [<c0034b40>] (ret_fast_syscall+0)

Code: e92dd800 e24cb004 e3520000 0a00000e (e5d03000)

---[ end trace da227214a82491b9 ]---

Segmentation fault

[[email protected]]#

上面出现了空指针:是在strncmp里出现了空指针,这个空指针是init_name;但是我们在我的device.c里已经.init_name="my_dev",为什么还是空指针呢?接下来看内核代码:

首先是找device_register:

技术分享

进入上面的device_add函数:会有下面的代码:

技术分享

上面的代码就是把不为空的init_name,赋值给dev_set_name,然后自身的值变为NULL。所以,我们的程序出现空指针的原因。这个值被赋值到了成员kobj.name:

技术分享

重新编译安装:

技术分享

我们该了之后,看到了,我们的驱动使能了我们的设备,设备开始工作了。这样我们就实现了该功能。

流程:当我们往总线加设备的时候,我的总线会把设备和总线上的驱动,一一进行匹配,当匹配成功的时候,就会去调用驱动的my_probe这个函数。看到了打印的出现。

 

上面是先有驱动,再有设备,工作的。

那么现有设备,再有驱动呢。同样能实现:

技术分享

以上是关于8.总线设备驱动模型的主要内容,如果未能解决你的问题,请参考以下文章

linux设备驱动模型之平台总线实践环节

面向对象地分析Linux内核设备驱动——Linux内核设备模型与总线

面向对象地分析Linux内核设备驱动——Linux内核设备模型与总线

第四季-专题13-总线设备驱动模型

让天堂的归天堂,让尘土的归尘土——谈Linux的总线设备驱动模型

总线设备驱动模型