3.1总线、设备、驱动框架

文章介绍了Linux内核中总线、设备和驱动的关系,通过structbus_type、structdevice和structdevice_driver结构体抽象表示。当设备连接到总线,系统会寻找匹配的驱动进行绑定,实现驱动与设备的解耦。文章提供了注册、注销总线、设备和驱动的函数示例,并通过一个简单的驱动代码示例说明了驱动的probe和remove函数的用途。
摘要由CSDN通过智能技术生成

总线、设备、驱动之间的关系

如下是USB总线的示意图,USB总线上可以连接各种各样的USB设备,为了让这些设备能正常工作,系统又再USB总线上注册个各种各样的设备驱动,当一个设备接入USB总线,USB总线会根据设备信息去遍历注册到USB总线上的驱动,然后找到一个与设备匹配的驱动,并将其于设备绑定,用于驱动设备。
在这里插入图片描述
再一个实际的计算机系统中往往还存在其他各种各样的总线,如SPI总线、IIC总线等,Linux内核中使用struct bus_type、struct device、struct device_driver来对总线、设备、设备驱动进行抽象,这3个对象都内嵌了struct kobject或struct kset对象,因此可以在/sys/目录生成相应的文件用于描述3者之间的关系
通过前面的内容不难发现引入总线、设备、驱动框架后设备和设备驱动分别挂载在对应的总线上,实现了驱动和设备的分离,降低了设备和驱动之间的耦合(这也是总线、设备、驱动框架的一大优点)。

struct bus_type对象

struct bus_type 对象表示一条总线,其中主要包括以下成员:

//总线名称
const char *name;
//完成设备和驱动进行匹配,返回非0值表示匹配成功
int (*match)(struct device *dev, struct device_driver *drv);

注册、注销总线

通过下列函数可以实现总线的注册和注销

//注册总线
int bus_register(struct bus_type *bus)
//注销总线
void bus_unregister(struct bus_type *bus)

struct device对象

struct device 对象表示一个设备,其中主要包括以下成员:

//设备名称
const char *init_name;
//所所属的总线
struct bus_type *bus;
//平台数据,一般记录设备的私有信息
void *platform_data;
//驱动数据,一般用于记录驱动的私有信息
void *driver_data;
//设备释放函数,在注销设备时被调用,若不提供此函数在卸载时可能会抱错
void (*release)(struct device *dev);
//指向此设备的设备树节点
struct device_node *of_node;

注册、注销设备

通过以下函数可以完成设备的注册和注销:

//注册设备
int device_register(struct device *dev)
//注销设备
void device_unregister(struct device *dev)

struct device_driver对象

struct device_driver对象表示一个驱动,其中主要包括以下成员:

//驱动名称
const char *name;
//所依赖的总线
struct bus_type *bus;
//设备树匹配表,在使用设备树时用此参数与设备树描述的设备的compatible属性进行匹配
const struct of_device_id *of_match_table;
//设备和驱动匹配成功执行
int (*probe)(struct device *dev);
//设备或驱动卸载时执行
int (*remove)(struct device *dev);

注册、注销驱动

通过下面的函数可以实现驱动的注册和注销

//注册驱动
int driver_register(struct device_driver *drv)
//注销驱动
void driver_unregister(struct device_driver *drv)

驱动代码实现

此节的驱动代码分为3个部分,分别时描述总线的代码、描述设备的代码、设备驱动代码

总线代码

描述总线的代码主要向系统注册一条总线(这条总线是高度抽象的,没有实现具体的功能),在代码中主要关注总线的注册、注销以及总线的设备驱动匹配函数,具体代码如下:

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

//匹配设备和驱动
static int mybus_match(struct device *dev, struct device_driver *drv)
{
    //返回非0值表示匹配成功
    return 1;
}

struct bus_type mybus = {
    .name = "mybus",
    .match = mybus_match,
};
EXPORT_SYMBOL(mybus);

static int __init bus_init(void)
{
    //注册总线
    return bus_register(&mybus);
}

static void __exit bus_exit(void)
{
    //注销总线
    bus_unregister(&mybus);
}

module_init(bus_init);
module_exit(bus_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("csdn");
MODULE_DESCRIPTION("This module is mybus");
MODULE_ALIAS("mybus");

设备代码

描述设备的代码主要向系统注册一个设备(注册的是一个高度抽象的设备,不包括具体的特性),在代码中主要关注设备的注册、注销、以及设备释放函数的实现,具体代码如下:

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

extern struct bus_type mybus;

//设备释放函数,在设备卸载时执行
static void mydev_release(struct device *dev)
{
    printk("mydev release\r\n");
}

static struct device mydev = 
{
    .init_name = "mydev",
    .bus = &mybus,
    .release = mydev_release,
};


static int __init dev_init(void)
{
    //注册设备
    return device_register(&mydev);
}

static void __exit dev_exit(void)
{
    //注销设备
    device_unregister(&mydev);
}

module_init(dev_init);
module_exit(dev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("csdn");
MODULE_DESCRIPTION("This module is mydev");
MODULE_ALIAS("mydev");

驱动代码

驱动代码主要完成驱动的注册、注销(注册的时一个高度抽象的驱动,没有实现具体的驱动功能),驱动代码主要关注驱动的注册、注销,以及probe函数和remove函数的实现,probe函数在设备和驱动匹配成功后被调用,这时驱动应根据具体的设备执行相应的初始化操作,remove函数在驱动或设备被卸载时执行,这时驱动程序应释放初始化过程中分配的资源,如下是一个最近的的设备驱动:

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

extern struct bus_type mybus;

//设备和驱动匹配成功后被调用,可以执行一些初始化操作
static int probe(struct device *dev)
{
    printk("mydrv %s\n", __FUNCTION__);
    return 0;
}

//设备或驱动被卸载时执行,释放初始化过程分配的资源
static int remove(struct device *dev)
{
    printk("mydrv %s\n", __FUNCTION__);
    return 0;
}

static struct device_driver mydrv = 
{
    .name = "mydrv",
    .bus = &mybus,
    .probe = probe,
    .remove = remove,
};

static int __init drv_init(void)
{
    //注册驱动
    return driver_register(&mydrv);
}

static void __exit drv_exit(void)
{
    //注销驱动
    driver_unregister(&mydrv);
}

module_init(drv_init);
module_exit(drv_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("csdn");
MODULE_DESCRIPTION("This module is mydrv");
MODULE_ALIAS("mydrv");

上机实验

  1. 从这里下载代码,并进行编译(这里涉及到同时编译多个内核模块的内容,请参考1.4模块间相互引用)中的模块编译方法),然后将代码拷贝到目标板跟文件系统。
  2. 执行命令insmod bus.ko加载总线模块,加载完成后可在/sys/bus/看到刚才注册的总线(mybus总线)
    3.
  3. 执行命令insmod dev.ko加载设备模块,加载完成后可在/sys/bus/mybus/devices/目录下看到刚才注册的设备
    在这里插入图片描述
  4. 执行命令insmod drv.ko加载驱动模块,加载成功后会执行驱动的probe函数(因为设备已经实现加载,所以设备和驱动成功匹配),同时在/sys/bus/mybus/drivers目录下可以看到刚才注册的驱动。

    5.执行命令rmmod dev.ko卸载设备,此时设备的release函数和驱动的remove函数被调用,此时再执行rmmod drv.ko不会有输出(因为设备驱动再卸载设备过程中已经解绑,驱动的remove函数已被调用)
    在这里插入图片描述

提示

在设备驱动框架中,设备和驱动都依赖于总线,所以模块加载时需要先加载总线,卸载时最后卸载总线,驱动和设备之间没有直接的依赖关系,来者的加载卸载顺序可以随意。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值