Bus Types
总线类型
Definition
~~~~~~~~~~
struct bus_type {
char * name;
struct subsystem subsys;
struct kset drivers;
struct kset devices;
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 (*hotplug) (struct device *dev, char **envp,
int num_envp, char *buffer, int buffer_size);
int (*suspend)(struct device * dev, pm_message_t state);
int (*resume)(struct device * dev);
};
int bus_register(struct bus_type * bus);
定义
----》
bus_type结构已发生加大变化,给出2.6.29版本的结构如下:
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 dev_pm_ops *pm;
struct bus_type_private *p;
};
(结构位于源代码/inculde/linux/device.h中,其它版本可以自行查阅)
注册函数:
int bus_register(struct bus_type * bus);
译者见解:
此处,我希望读者能够重温概述的[底层访问]一节。给出链接如下:
再给出bus_type_private结构:
struct bus_type_private {
struct kset subsys;
struct kset *drivers_kset;
struct kset *devices_kset;
struct klist klist_devices;
struct klist klist_drivers;
struct blocking_notifier_head bus_notifier;
unsigned int drivers_autoprobe:1;
struct bus_type *bus;
};
请读者尝试结合面向对象思想想一下,为什么要进行这样的修改。
Declaration
~~~~~~~~~~~
Each bus type in the kernel (PCI, USB, etc) should declare one static
object of this type. They must initialize the name field, and may
optionally initialize the match callback.
struct bus_type pci_bus_type = {
.name = "pci",
.match = pci_bus_match,
};
The structure should be exported to drivers in a header file:
extern struct bus_type pci_bus_type;
声明
----》
内核中的每一个总线类型(如PCI,USB等)都应该静态地申请如下对象。其中,
name和match两个成员是必须进行初始化的:
struct bus_type pci_bus_type = {
.name = "pci",
.match = pci_bus_match,
};
上述结构应该通过头文件的形式向驱动导出,如下:
extern struct bus_type pci_bus_type;
读者见解:
别混熬了extern和EXPORT_SYMBOL()它们的作用范围是不同的。extern使得变
量在所有包含该头文件的代码可见。而EXPORT_SYMBOL()则在模块加载时,对
变量进行了加工,使得变量在全内核代码中可见。这里静态定义的bus_type
一般都会EXPORT_SYMBOL。
Registration
~~~~~~~~~~~~
When a bus driver is initialized, it calls bus_register. This
initializes the rest of the fields in the bus object and inserts it
into a global list of bus types. Once the bus object is registered,
the fields in it are usable by the bus driver.
注册
----》
在总线驱动初始化时,会调用bus_register()函数。初始化过程中,总线对象
的其它成员(注:不包括回调)将会被初始化,该总线类型结构将会被加入到一
个全局的总线类型链表结构中。一旦总线对象被注册,其(结构)成员将会对总
线驱动可用。
译者见解:
总线驱动对应着桥接器,桥接器一般是板载的,所以总线的注册通常会在内核
初始化阶段发生。我们所做的驱动大多数都是特殊总线层以上的设备驱动。每
一个与特殊总线相关的总线结构对应着sys文件系统里的一个子系统, 当需要
驱动一个设备时,设备驱动者要善于利用内核已有的子系统,而不是麻木的去
自己实现一个子系统。
Callbacks
~~~~~~~~~
match(): Attaching Drivers to Devices
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The format of device ID structures and the semantics for comparing
them are inherently bus-specific. Drivers typically declare an array
of device IDs of devices they support that reside in a bus-specific
driver structure.
The purpose of the match callback is provide the bus an opportunity to
determine if a particular driver supports a particular device by
comparing the device IDs the driver supports with the device ID of a
particular device, without sacrificing bus-specific functionality or
type-safety.
When a driver is registered with the bus, the bus's list of devices is
iterated over, and the match callback is called for each device that
does not have a driver associated with it.
回调函数
--------》
设备ID的格式和用来比较它们的语义是因不同总线而异的。驱动通常会在他们的
结构中声明一个设备ID数组,用户表示它们所能够支持的设备的ID。
match回调的目的是为了给综艺一个机会去判定某一个特定的驱动是否支持某一个
特定的设备,这种判定是通过比较设备与驱动的ID来完成的。这种回调方式可以
在不牺牲某一特定总线功能(注:匹配过程可以实现某些与特殊总线相关的功能)
和安全性的前提下完成匹配。
当一个驱动被注册到一个总线时,该总线(结构)的设备链表成员将会被遍历,
(LDM核心)会为链表中每个没有与(其它)驱动绑定的设备调用match与当前驱动尝
试匹配。
Device and Driver Lists
~~~~~~~~~~~~~~~~~~~~~~~
The lists of devices and drivers are intended to replace the local
lists that many buses keep. They are lists of struct devices and
struct device_drivers, respectively. Bus drivers are free to use the
lists as they please, but conversion to the bus-specific type may be
necessary.
The LDM core provides helper functions for iterating over each list.
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 *));
These helpers iterate over the respective list, and call the callback
for each device or driver in the list. All list accesses are
synchronized by taking the bus's lock (read currently). The reference
count on each object in the list is incremented before the callback is
called; it is decremented after the next object has been obtained. The
lock is not held when calling the callback.
设备和驱动链表
--------------》
驱动和设备链表的目标是为了替代总线维护的许多静态链表。它们分别是device
和device_driver的链表。虽然总线驱动能够自由的使用上面的链表,我们还是有
必要将它们转变成与特定总线相关的类型。
LDM核心提供下面辅助函数来遍历每个链表:
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 *));
这些辅助函数分别遍历了相应的链表,同时在遍历过程中为每个设备或驱动都调用
了fn回调。所有对链表的访问都通过持有总线的锁来同步。在回函数fn调用之前,
每个对象的引用计数都会被增加,直到下个对象被访问时,当前对象的引用计数才
会回减。当调用回到函数时,不会持有锁(注:表示回调函数可中断可休眠)。
sysfs
~~~~~~~~
There is a top-level directory named 'bus'.
Each bus gets a directory in the bus directory, along with two default
directories:
/sys/bus/pci/
|-- devices
`-- drivers
Drivers registered with the bus get a directory in the bus's drivers
directory:
/sys/bus/pci/
|-- devices
`-- drivers
|-- Intel ICH
|-- Intel ICH Joystick
|-- agpgart
`-- e100
Each device that is discovered on a bus of that type gets a symlink in
the bus's devices directory to the device's directory in the physical
hierarchy:
/sys/bus/pci/
|-- devices
| |-- 00:00.0 -> ../../../root/pci0/00:00.0
| |-- 00:01.0 -> ../../../root/pci0/00:01.0
| `-- 00:02.0 -> ../../../root/pci0/00:02.0
`-- drivers
sys文件系统
-----------》
(在sys文件系统中)有一个名叫bus的顶层目录。
每一种总线类型都会在该目录下得到一个目录,同时也会拥有如下的两个子目录。
/sys/bus/pci/
|-- devices
`-- drivers
向总线注册的驱动也会得到一个目录,如下:
/sys/bus/pci/
|-- devices
`-- drivers
|-- Intel ICH
|-- Intel ICH Joystick
|-- agpgart
`-- e100
在总线上的每一个设备都会在总线的devices目录下得到一个连接到该设备物理
层的符号链接,如下:
/sys/bus/pci/
|-- devices
| |-- 00:00.0 -> ../../../root/pci0/00:00.0
| |-- 00:01.0 -> ../../../root/pci0/00:01.0
| `-- 00:02.0 -> ../../../root/pci0/00:02.0
`-- drivers
Exporting Attributes
~~~~~~~~~~~~~~~~~~~~
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 drivers can export attributes using the BUS_ATTR macro that works
similarly to the DEVICE_ATTR macro for devices. For example, a definition
like this:
static BUS_ATTR(debug,0644,show_debug,store_debug);
is equivalent to declaring:
static bus_attribute bus_attr_debug;
This can then be used to add and remove the attribute from the bus's
sysfs directory using:
int bus_create_file(struct bus_type *, struct bus_attribute *);
void bus_remove_file(struct bus_type *, struct bus_attribute *);
导出属性文件
------------》
属性结构:
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_ATTR()导出属性结构(这里还没导出到sys文件系统),该导
出方法跟DEVICE_ATTR()类似,例如:
static BUS_ATTR(debug,0644,show_debug,store_debug);
这样等价于以下声明:
static bus_attribute bus_attr_debug;
下面接口可以用于向sys文件系统导出和移除属性文件:
int bus_create_file(struct bus_type *, struct bus_attribute *);
void bus_remove_file(struct bus_type *, struct bus_attribute *);
译者见解:
结构代表着总线的一个属性,成员attr包含属性的相关信息。成员show和store分别
是导出到sys文件系统时读和写的方法。通过实现读的方法,总线驱动可以通过文件
读操作向用户空间输出必要的信息,这些信息可以是驱动的状态, 驱动中某个关键
变量的值等等。同样的,通过实现读方法, 用户空间可以通过文件写操作改变驱动
的状态,修改其中某个关键变量的值。善用这两个方法可以使驱动变得更加灵活。