总线是处理器和一个或多个设备之间的通道,在设备模型中,所有的设备都通过总线相连,甚至是内部的虚拟"platform"总线。可以通过ls -l /sys/bus看到系统加载的所有总线。
drwxr-xr-x root root 1970-01-01 00:02 platform
drwxr-xr-x root root 1970-01-01 00:02 spi
drwxr-xr-x root root 1970-01-01 00:02 scsi
drwxr-xr-x root root 1970-01-01 00:02 usb
drwxr-xr-x root root 1970-01-01 00:02 serio
drwxr-xr-x root root 1970-01-01 00:02 i2c
drwxr-xr-x root root 1970-01-01 00:02 mmc
drwxr-xr-x root root 1970-01-01 00:02 sdio
drwxr-xr-x root root 1970-01-01 00:02 ac97
总线可以相互插入。设备模型展示了总线和它们所控制的设备之间的实际连接。在Linux 设备模型中,总线由bus_type 结构表示,定义在 <linux/device.h> :
struct bus_type {
const char *name; /*总线类型名称*/
struct bus_attribute *bus_attrs; /*总线属性*/
struct device_attribute *dev_attrs; /*设备属性,指为每个加入总线的设备建立的默认属性链表*/
struct driver_attribute *drv_attrs; /*驱动程序属性*/
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 (*suspend)(struct device *dev, pm_message_t state); /*电源管理函数*/
int (*suspend_late)(struct device *dev, pm_message_t state);
int (*resume_early)(struct device *dev);
int (*resume)(struct device *dev);
struct pm_ext_ops *pm;
struct bus_type_private *p;
};
1,总线的注册和删除,总线的主要注册步骤:
(1)申明和初始化bus_type 结构体。只有很少的bus_type 成员需要初始化,大部分都由设备模型核心控制。但必须为总线指定名字及一些必要的方法。例如:
struct bus_type ldd_bus_type = {
.name = "ldd",
.match = ldd_match,
.uevent = ldd_uevent,
};
(2)调用bus_register函数注册总线。int bus_register(struct bus_type *bus),该调用可能失败,所以必须始终检查返回值。
ret = bus_register(&ldd_bus_type);
if (ret)
return ret;
若成功,新的总线子系统将被添加进系统,之后可以向总线添加设备。当必须从系统中删除一个总线时,调用:
void bus_unregister(struct bus_type *bus);
2,总线方法
在 bus_type 结构中定义了许多方法,它们允许总线核心作为设备核心与单独的驱动程序之间提供服务的中介,主要介绍以下两个方法: int (*match)(struct device * dev, struct device_driver * drv);
/*当一个新设备或者驱动被添加到这个总线时,这个方法会被调用一次或多次,若指定的驱动程序能够处理指定的设备,则返回非零值。*/
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
/*在为用户空间产生热插拔事件之前,这个方法允许总线添加环境变量*/
对设备和驱动的迭代:若要编写总线层代码,可能不得不对所有已经注册到总线的设备或驱动进行一些迭代操作,这可能需要仔细研究嵌入到 bus_type 结构中的其他数据结构,但最好使用内核提供的辅助函数:
int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data, int (*fn)(struct device *, void *));
int bus_for_each_drv(struct bus_type *bus, struct device_driver *start, void *data, int (*fn)(struct device_driver *, void *));
/*这两个函数迭代总线上的每个设备或驱动程序(内部分别有next_device和next_driver),将关联的device或device_driver传递给 fn,同时传递data 值。若start为NULL,则从第一个设备开始;否则从start之后的第一个设备开始。若fn返回非零值,迭代停止并且那个值从bus_for_each_dev 或bus_for_each_drv 返回。*/
3,总线属性
几乎Linux 设备模型中的每一层都提供添加属性的函数,总线层也不例外。bus_attribute 类型定义在<linux/device.h> 如下:
struct bus_attribute {
struct attribute attr;
ssize_t (*show)(struct bus_type *, char * buf);
ssize_t (*store)(struct bus_type *, const char * buf, size_t count);
};
内核提供了一个宏在编译时创建和初始化bus_attribute 结构:
BUS_ATTR(_name,_mode,_show,_store)/*这个宏声明一个结构,将bus_attr_作为给定_name 的前缀来创建总线的真正名称*/
/*总线的属性必须显式调用bus_create_file 来创建:*/
int bus_create_file(struct bus_type *bus, struct bus_attribute *attr);
/*删除总线的属性调用:*/
void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr);
例如创建一个包含源码版本号简单属性方法如下:
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); //得到bus_attr_version
/*在模块加载时创建属性文件:*/
if (bus_create_file(&ldd_bus_type, &bus_attr_version))
printk(KERN_NOTICE "Unable to create version attribute/n");
/*这个调用创建一个包含 lddbus 代码的版本号的属性文件(/sys/bus/ldd/version)*/
本文来自优快云博客,转载请标明出处:http://blog.youkuaiyun.com/funy_liu/archive/2010/02/25/5322040.aspx