关于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