您的位置:首页 > 运维架构 > Linux

驱动学习回顾——Linux下bus设备模型

2017-08-21 13:14 567 查看
关于Linux的驱动学习有三个点是应该掌握的,即:总线驱动设备。不管是一些物理总线(如:I2C,SPI等)的抽象,还是为了增加设备“容量”而定义的虚拟总线(plat-from),都是围绕着这三个进行。即:一个符合Linux设备驱动模型的device和device_dvrier都挂在一个bus上,由bus来进行两者匹配,进行双向绑定。

下面简单地给出自己简单的学习过程。

一、总线

总线这个概念在内核中在
include/linux/device.h
路径下是由bus_type结构体表示。详情可自行阅读源代码。



name
是总线的名字,
match
函数指针是device_driver和device的匹配规则,具体地说,当一个device被加入时,会和bus上的所有device_driver进行匹配操作,如果有device_driver能支持这个device,则匹配成功,match返回非0值。

下面给出一段创建总线的简单代码:

/*********************************************************************************
*      Copyright:  (C) 2017 TangBin<tangbinmvp@gmail.com>
*                  All rights reserved.
*
*       Filename:  bus.c
*    Description:  This file
*
*        Version:  1.0.0(08/20/2017)
*         Author:  TangBin <tangbinmvp@gmail.com>
*      ChangeLog:  1, Release initial version on "08/20/2017 07:53:13 PM"
*
********************************************************************************/

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>

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

struct bus_type bus_type_t = {
.name = "bus_t",
.match = match_t,
};
EXPORT_SYMBOL(bus_type_t);

int bus_t_init(void)
{
int result;
result = bus_register(&bus_type_t);
return result;
}

void bus_t_exit(void)
{
bus_unregister(&bus_type_t);
}

module_init(bus_t_init);
module_exit(bus_t_exit);
MODULE_AUTHOR("Tangbin");
MODULE_LICENSE("GPL");


解读:

1、从
module_init
开始看,进入到
bus_t_init
中,由
bus_register(&bus_type_t)
函数注册总线。(
bus_register
函数在
drivers/base/bus.c
路径下)

2、
bus_type_t
是在
bus_type
结构体定义,里面定义了成员
name
match
。 name即为这个bus的名字。

3、
match
匹配了
match_t
函数,当有新的驱动(或设备)加到这条总线时,总线就会调用
match
指向的函数,一一匹配是否有对应的驱动(或设备)存在,匹配成功则返回非0值,比较的方式就是检测设备的名字
dev->kobj.name
和驱动的名字
drv->name
是否相等。在这里设备的名字是
dev->kobj.name
是因为在
drivers/base/core.c
device_add
的函数中:

if (dev->init_name) {
dev_set_name(dev, "%s", dev->init_name);
dev->init_name = NULL;
}


dev->init_name
会被赋值为
NULL
导致加载模块后引发空指针异常。

(关于kobject:http://blog.chinaunix.net/uid-20672257-id-3147337.html 比较复杂……)

4、
EXPORT_SYMBOL(bus_type_t)
,由于几个模块的依赖,使用了内核提供的机制,以
EXPORT_SYMBOL
内定义的函数或者符号对全部内核代码公开,不用修改内核代码即可在其它模块中调用。

二、驱动

struct device_driver
在内核中表示驱动



name
为驱动名字,
bus
指驱动所在的哪条总线,
probe
指向的函数则是匹配完后进行一系列初始化的函数,相应地,
remove
指向的函数是释放资源的作用。

简单驱动代码:

/*********************************************************************************
*      Copyright:  (C) 2017 TangBin<tangbinmvp@gmail.com>
*                  All rights reserved.
*
*       Filename:  driver.c
*    Description:  This file
*
*        Version:  1.0.0(08/20/2017)
*         Author:  TangBin <tangbinmvp@gmail.com>
*      ChangeLog:  1, Release initial version on "08/20/2017 07:55:39 PM"
*
********************************************************************************/
#include <linux/device.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>

extern struct bus_type bus_type_t;

int probe_t(struct device *dev)
{
printk("hello,world\n");
return 0;
}

struct device_driver driver_t = {
.name = "device",
.bus = &bus_type_t,
.probe = probe_t,
};

int driver_t_init(void)
{
int result;
result = driver_register(&driver_t);
return result;
}

void driver_t_exit(void)
{
driver_unregister(&driver_t);
}

module_init(driver_t_init);
module_exit(driver_t_exit);
MODULE_AUTHOR("Tangbin");
MODULE_LICENSE("GPL");


这里同样有
driver_register
driver_unregister
进行注册和注销。

三、设备

struct device
在内核中表示设备



init_name
设备名,
bus
为所在总线。

简单设备代码:

/*********************************************************************************
*      Copyright:  (C) 2017 TangBin<tangbinmvp@gmail.com>
*                  All rights reserved.
*
*       Filename:  device.c
*    Description:  This file
*
*        Version:  1.0.0(08/20/2017)
*         Author:  TangBin <tangbinmvp@gmail.com>
*      ChangeLog:  1, Release initial version on "08/20/2017 08:06:25 PM"
*
********************************************************************************/

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>

extern struct bus_type bus_type_t;

struct device device_t = {
.init_name = "device",
.bus = &bus_type_t,
};

int device_t_init(void)
{
int result;
result = device_register(&device_t);
return result;
}

void device_t_exit(void)
{
device_unregister(&device_t);
}

module_init(device_t_init);
module_exit(device_t_exit);
MODULE_AUTHOR("Tangbin");
MODULE_LICENSE("GPL");


同样有
device_register
device_unregister
进行注册和注销。

使用Makefile编译这三个模块:

LINUX_SRC ?= ../my-linux-3.0
CROSS_COMPILE=/opt/buildroot-2012.08/arm920t/usr/bin/arm-linux-

obj-m := bus.o device.o driver.o

modules:
@make -C  $(LINUX_SRC) M=`pwd` modules
@make clean

clean:
rm -f *.ko.* *.o *mod.c *.order *.symvers


bus.ko
driver,ko
device.ko
下到开发板,依次
insmod


会在串口打印出
hello,world
。 哈哈,又是另一种“hello,world”的方法



简单分析了bus、driver、device之间的联系,实际上在Linux中内核的处理是很复杂的,在这只是将其一小部分拿出来分析编写了一下,目的是为了理解这三者的关系。

参考资料:

http://www.wowotech.net/linux_kenrel/bus.html

http://bbs.csdn.net/topics/390599875

http://blog.csdn.net/skdkjzz/article/details/38927907
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux 驱动