目录
一. 总线简介
总线是处理器和设备之间的通道,在设备模型中, 所有的设备都通过总线相连, 甚至是内部的虚拟“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;
};
**结论:**
- bus_type继承于kset;
- bus_type包含了两条链表,分别为klist_devices和klist_drivers。
- match()函数的两个参数为bus_type下的两条链表(devices和drivers),当插入device或driver时,均会执行该函数进行匹配。
- probe()函数:当match()函数配对成功后,会执行。(bus_type和driver中均有该接口)
- bus_type和kset的继承关系:
-
结构体来看:subsys_private包含一个kset成员。
- 接口函数来看:初始化、注册、注销、都会首先做kset的工作。
- bus_type继承了kset的name、kref、sd、uevent等。
- 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");