设备驱动模型--bus_type详解

目录

一. 总线简介

二. 内核数据结构及接口

2.1 内核数据结构

2.2 内核接口

2.2.1 总线注册,注销等

2.2.2 总线方法

2.2.3 总线属性

三. 实例

3.1 Makefile

3.2 实例1

3.3 效果​


一. 总线简介

    总线是处理器和设备之间的通道,在设备模型中, 所有的设备都通过总线相连, 甚至是内部的虚拟“platform”总线。 在 Linux 设备模型中, 总线由 bus_type 结构表示, 定义在 inux/device.h。

二. 内核数据结构及接口

2.1 内核数据结构

struct bus_type {
    const char        *name;       /*总线名称*/
    const char        *dev_name;
    struct device        *dev_root;
    struct device_attribute    *dev_attrs;    /* use dev_groups instead */
    const struct attribute_group **bus_groups;   /*总线属性*/
    const struct attribute_group **dev_groups;   /*设备属性*/
    const struct attribute_group **drv_groups;   /*驱动属性*/

    int (*match)(struct device *dev, struct device_driver *drv);
    int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
    int (*probe)(struct device *dev);
    int (*remove)(struct device *dev);
    void (*shutdown)(struct device *dev);

    int (*online)(struct device *dev);
    int (*offline)(struct device *dev);

    int (*suspend)(struct device *dev, pm_message_t state);
    int (*resume)(struct device *dev);

    const struct dev_pm_ops *pm;

    const struct iommu_ops *iommu_ops;

    struct subsys_private *p;
    struct lock_class_key lock_key;
};

struct subsys_private {
    struct kset subsys;
    struct kset *devices_kset;
    struct list_head interfaces;
    struct mutex mutex;

    struct kset *drivers_kset;
    struct klist klist_devices;
    struct klist klist_drivers;
    struct blocking_notifier_head bus_notifier;
    unsigned int drivers_autoprobe:1;
    struct bus_type *bus;

    struct kset glue_dirs;
    struct class *class;
};


**结论:**

  1.  bus_type继承于kset;
  2.  bus_type包含了两条链表,分别为klist_devices和klist_drivers。
  3.  match()函数的两个参数为bus_type下的两条链表(devices和drivers),当插入device或driver时,均会执行该函数进行匹配。
  4.  probe()函数:当match()函数配对成功后,会执行。(bus_type和driver中均有该接口)
  5.  bus_type和kset的继承关系:
  • 结构体来看:subsys_private包含一个kset成员。

  • 接口函数来看:初始化、注册、注销、都会首先做kset的工作。
  • bus_type继承了kset的name、kref、sd、uevent等。
  1. bus_kset
  • bus_kset 表明 /sys/bus/目录。
  • int __init buses_init(void) 创建重要的顶层目录 /sys/bus。
  • 表明所有的bus_type对象都在 bus_kset 集合中

2.2 内核接口

2.2.1 总线注册,注销等

/*1. 总线的注册使用
  注:若成功,新的总线将被添加进系统,并可在sysfs 的 /sys/bus 下看到。
*/
extern int __must_check bus_register(struct bus_type *bus);

/*2. 总线的删除使用*/
extern void bus_unregister(struct bus_type *bus);

/**/
extern int __must_check bus_rescan_devices(struct bus_type *bus);

int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data,
             int (*fn)(struct device *dev, void *data));
             
int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
             void *data, int (*fn)(struct device_driver *, void *));

2.2.2 总线方法

/*1. match()
当一个新设备或者驱动被添加到这个总线时,该方法被调用。用于判断指定的驱动程序是否能处理指定的设备。
若可以,则返回非零值。
*/
int (*match)(struct device *dev, struct device_driver *drv);

/*2. uevent()
在为用户空间产生热插拔事件之前,这个方法允许总线添加环境变量。
*/
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);


2.2.3 总线属性

/*1. 在内核中表示的数据结构*/
struct bus_attribute {
    struct attribute    attr;
    ssize_t (*show)(struct bus_type *bus, char *buf);
    ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count);
};

/*2. 创建属性*/
extern int __must_check bus_create_file(struct bus_type *,
                    struct bus_attribute *);

/*3. 删除属性*/
extern void bus_remove_file(struct bus_type *, struct bus_attribute *);

三. 实例

3.1 Makefile

ifneq ($(KERNELRELEASE),)
    obj-m := bus_test.o
else
    PWD  := $(shell pwd)
KDIR := /lib/modules/$(shell uname -r)/build

all:
    $(MAKE) -C $(KDIR) M=$(PWD) modules

clean:
    rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions modules.order Module.symvers
endif

3.2 实例1

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

static char *version = "version: 1.0";
EXPORT_SYMBOL_GPL(version);

static int my_match(struct device *dev, struct device_driver *driver)
{
    if (!dev || !driver || (!dev && !driver)) {
        printk("[ my_bus: my_match ] have NULL. \n");
        return -1;
    }

    printk("[ my_bus: my_match ] device name: %s: driver name: %s \n",
        dev_name(dev), driver->name);
    
    return !strncmp(dev_name(dev), driver->name, strlen(driver->name));
}


int my_bus_probe(struct device *dev)
{
    printk("[ my_bus: my_match ] device name: %s: driver name: %s \n",
        dev_name(dev), dev->driver->name);
        
    dev->driver->probe(dev);
    return 0;
}

struct bus_type my_bus_type = {
    .name = "my_bus",
    .match = my_match,
    .probe = my_bus_probe,
};
EXPORT_SYMBOL_GPL(my_bus_type);


static ssize_t show_bus_version(struct bus_type *bus, char *buf)
{
    return snprintf(buf, PAGE_SIZE, "%s \n", version);
}

static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);

static __init int my_bus_init(void)
{
    int err = 0;
    printk(KERN_NOTICE "my_bus_init \n");
    
    /*register bus*/
    err = bus_register(&my_bus_type);
    if (err)
        return err;

    /*create attr file*/
    err = bus_create_file(&my_bus_type, &bus_attr_version);
    if (err) {
        printk(KERN_NOTICE "Fail to create version attribute!\n");
        bus_unregister(&my_bus_type);
        return err;
    }    

    return err;
}

static __exit void my_bus_exit(void)
{
    printk(KERN_NOTICE "my_bus_exit \n");
    bus_remove_file(&my_bus_type, &bus_attr_version);
    bus_unregister(&my_bus_type);
}


module_init(my_bus_init);
module_exit(my_bus_exit);
MODULE_AUTHOR("vec");
MODULE_LICENSE("Dual BSD/GPL");

3.3 效果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值