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

21 篇文章 0 订阅
18 篇文章 1 订阅

关于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 结构体定义,里面定义了成员namematch 。 name即为这个bus的名字。
3、match 匹配了 match_t 函数,当有新的驱动(或设备)加到这条总线时,总线就会调用match指向的函数,一一匹配是否有对应的驱动(或设备)存在,匹配成功则返回非0值,比较的方式就是检测设备的名字dev->kobj.name和驱动的名字drv->name是否相等。在这里设备的名字是dev->kobj.name 是因为在 drivers/base/core.cdevice_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_registerdriver_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_registerdevice_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.kodriver,kodevice.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

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值