Linux总线驱动设计(1)-总线驱动模型
来源:互联网 发布:2016酒店业数据 编辑:程序博客网 时间:2024/05/22 15:12
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、注销总线
总线的注销使用:
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);
安装这个模块后,使用:
bus: module license 'unspecified' taints kernel.Disabling lock debugging due to kernel taintbus: Unknown symbol bus_unregisterbus: Unknown symbol bus_registerinsmod: 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、创建一个驱动
#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后编译
4、设备
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、设备与驱动的匹配
int my_macth(struct device * dev, struct device_driver * drv){return !strncmp(dev->init_name, drv->name,strlen(drv->name));}
#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 00000000pgd = c39c4000[00000000] *pgd=339bf031, *pte=00000000, *ppte=00000000Internal error: Oops: 17 [#1]last sysfs file: /sys/devices/platform/soc-audio/sound/card0/mixer/devModules linked in: device(+) driver busCPU: 0 Not tainted (2.6.32.2-FriendlyARM #17)PC is at strncmp+0x14/0x68LR is at my_macth+0x2c/0x38 [bus]pc : [<c014f6dc>] lr : [<bf000064>] psr: 20000013sp : c39a7e38 ip : c39a7e48 fp : c39a7e44r10: 00000000 r9 : 00000000 r8 : bf00c070r7 : c0187330 r6 : bf00c068 r5 : bf00c068 r4 : bf0060a4r3 : 00000000 r2 : 00000006 r1 : bf0060a4 r0 : 00000000Flags: nzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment userControl: c000717f Table: 339c4000 DAC: 00000015Process insmod (pid: 682, stack limit = 0xc39a6270)Stack: (0xc39a7e38 to 0xc39a8000)7e20: c39a7e5c c39a7e487e40: bf000064 c014f6d8 bf0060ac bf00c068 c39a7e74 c39a7e60 c0187364 bf0000487e60: 00000000 c39a7e78 c39a7e9c c39a7e78 c0186680 c0187340 c38feec8 c394f5747e80: bf00c068 bf00c09c bf00c108 00000000 c39a7eb4 c39a7ea0 c0187408 c01866247ea0: c04ce058 bf00c068 c39a7ec4 c39a7eb8 c0186474 c01873b4 c39a7f0c c39a7ec87ec0: c0184cb0 c0186458 00000000 c034b8f4 c39a7ef4 c39a7ee0 c014b490 c014c44c7ee0: bf00c068 bf00c068 00191e54 bf00c108 c39a6000 c04a3980 00000000 bf00c01c7f00: c39a7f24 c39a7f10 c0184ed8 c01849a8 00000969 00191e54 c39a7f34 c39a7f287f20: bf00c030 c0184ecc c39a7f7c c39a7f38 c003032c bf00c02c 00000000 000000007f40: 00000000 00000969 00191e54 bf00c108 00000000 00000969 00191e54 bf00c1087f60: 00000000 c00310c8 c39a6000 00000000 c39a7fa4 c39a7f80 c0070344 c00303007f80: c009b6b0 c009b5a0 00000000 00000000 bead6eb8 00000080 00000000 c39a7fa87fa0: c0030f20 c0070284 00000000 00000000 001aafd8 00000969 00191e54 000000007fc0: 00000000 00000000 bead6eb8 00000080 bead6eb4 bead6eb8 00000001 bead6eb47fe0: 00000069 bead6b7c 00020d48 000094b4 60000010 001aafd8 3055b031 3055b431Backtrace: [<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:00000000Code: 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));}
重新编译后,再安装,最后安装设备的时候会打印出一条信息,到这里就完成了总线的驱动了!
- Linux总线驱动设计(1)-总线驱动模型
- Linux驱动模型 - 总线
- Linux驱动Platform总线模型
- Linux 设备总线驱动模型
- Linux总线设备驱动模型
- Linux总线、设备、驱动模型
- Linux总线设备驱动模型
- Linux 设备总线驱动模型
- Linux总线设备驱动模型
- Linux总线、设备、驱动模型
- linux嵌入式驱动-总线设备驱动模型
- linux设备总线驱动模型 之 platform总线驱动
- linux设备总线驱动模型 之 platform总线驱动
- linux设备总线驱动模型 之 platform总线驱动
- 13.总线设备驱动模型(1)-总线设备驱动模型
- Linux 设备模型 --- 总线设备驱动模型 --- 总线
- 总线设备驱动模型
- 平台总线驱动模型
- 什么是DAO
- unity3D学习笔记之六 Material(材质)的几种RenderingMode(渲染模式)
- Android 自定义NumberPicker
- Linux下的crond和crontab
- matlab图像镜像
- Linux总线驱动设计(1)-总线驱动模型
- HTML CSS + DIV实现整体布局
- Java 1.8 堆内存变化及GC算法
- json 与jsonp 区别与原理
- 巨杉数据库SequoiaDB的常用操作
- Android AutoLayout
- ActiveMQ
- R6010 -abort() has been called错误
- Kotlin——初始Kotlin及Android Studio配置环境开发HelloKotlin