现在的位置: 首页 > 综合 > 正文

Documentation-driver-model-porting

2013年12月11日 ⁄ 综合 ⁄ 共 16887字 ⁄ 字号 评论关闭

Chinese translated version of Documentation-driver-model-porting

If you have any comment or update to the content, please contact the
original document maintainer directly.  However, if you have a problem
communicating in English you can also ask the Chinese maintainer for
help.  Contact the Chinese maintainer if this translation is outdated
or if there is a problem with the translation.

Chinese maintainer:  <steven910812@gmail.com>
---------------------------------------------------------------------
Documentation-driver-model-porting的中文翻译

如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文
交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻
译存在问题,请联系中文版维护者。

中文版维护者: 徐唯  <steven910812@gmail.com>
中文版翻译者: 徐唯  <steven910812@gmail.com>
中文版校译者: 徐唯  <steven910812@gmail.com>

以下为正文
---------------------------------------------------------------------

Porting Drivers to the New Driver Model
将驱动程序移植到新的驱动程序模型

Patrick Mochel

7 January 2003

Overview
概述

Please refer to Documentation/driver-model/*.txt for definitions of
various driver types and concepts. 
请查阅Documentation/driver-model/*.txt中相关各种驱动器类型和概念。

Most of the work of porting devices drivers to the new model happens
at the bus driver layer. This was intentional, to minimize the
negative effect on kernel drivers, and to allow a gradual transition
of bus drivers.
大多数移植设备驱动程序到一个新模型的工作发生在总线驱动层。这是有意为之,以尽量减少内核驱动程序所受到的负面影响,并总线驱动逐步转换。

In a nutshell, the driver model consists of a set of objects that can
be embedded in larger, bus-specific objects. Fields in these generic
objects can replace fields in the bus-specific objects. 
简而言之,驱动模型由一组可以嵌入更大,总线特定对象的对象。这些通用对象的字段可以取代字段在总线特定对象。

The generic objects must be registered with the driver model core. By
doing so, they will exported via the sysfs filesystem. sysfs can be
mounted by doing 

# mount -t sysfs sysfs /sys

通用对象必须在驱动模型的核心中注册。将经由sysfs文件系统导出。通过输入# mount -t sysfs sysfs /sys
可以安装sysfs。
# mount -t sysfs sysfs /sys

The Process
过程

Step 0: Read include/linux/device.h for object and function definitions. 
第0步:读include/linux/device.h中对象与函数的相关定义

Step 1: Registering the bus driver. 
- Define a struct bus_type for the bus driver.

struct bus_type pci_bus_type = {
        .name           = "pci",
};

- Register the bus type.
  This should be done in the initialization function for the bus type,
  which is usually the module_init(), or equivalent, function. 

static int __init pci_driver_init(void)
{
        return bus_register(&pci_bus_type);
}

subsys_initcall(pci_driver_init);

static int __init pci_driver_init(void)
{
        return bus_register(&pci_bus_type);
}

subsys_initcall(pci_driver_init);
  The bus type may be unregistered (if the bus driver may be compiled
  as a module) by doing:

     bus_unregister(&pci_bus_type);

- Export the bus type for others to use. 

  Other code may wish to reference the bus type, so declare it in a 
  shared header file and export the symbol.

From include/linux/pci.h:

extern struct bus_type pci_bus_type;

From file the above code appears in:

EXPORT_SYMBOL(pci_bus_type);

- This will cause the bus to show up in /sys/bus/pci/ with two
  subdirectories: 'devices' and 'drivers'.

# tree -d /sys/bus/pci/
/sys/bus/pci/
|-- devices
`-- drivers

第1步:注册总线驱动
为总线驱动定义一个结构体“bus_type”:
struct bus_type pci_bus_type = {
        .name           = "pci",
};
- 注册总线类型。
  这应该是做在初始化函数中的总线类型,这通常是宏module_init(),或同等功能。

  总线类型可能被注销(如果总线驱动器可被编译作为一个模块)通过:
bus_unregister(&pci_bus_type);

- 导出该总线类型供他人使用。
  其他代码也许会想要使用该总线类型,因此在一个共享的头文件中对该类型进行声明。

从 include/linux/pci.h:

extern struct bus_type pci_bus_type;

从上述代码所在的文件:

EXPORT_SYMBOL(pci_bus_type);
- 这会使得总线在/sys/bus/pci/目录下出现两个子目录:'devices' 和 'drivers'。

# tree -d /sys/bus/pci/
/sys/bus/pci/
|-- devices
`-- drivers

Step 2: Registering Devices. 

struct device represents a single device. It mainly contains metadata
describing the relationship the device has to other entities. 

- Embed a struct device in the bus-specific device type. 

struct pci_dev {
       ...
       struct  device  dev;            /* Generic device interface */
       ...
};

  It is recommended that the generic device not be the first item in 
  the struct to discourage programmers from doing mindless casts
  between the object types. Instead macros, or inline functions,
  should be created to convert from the generic object type.

  #define to_pci_dev(n) container_of(n, struct pci_dev, dev)

or 

static inline struct pci_dev * to_pci_dev(struct kobject * kobj)
{
return container_of(n, struct pci_dev, dev);
}

  This allows the compiler to verify type-safety of the operations 
  that are performed (which is Good).

- Initialize the device on registration.

  When devices are discovered or registered with the bus type, the 
  bus driver should initialize the generic device. The most important
  things to initialize are the bus_id, parent, and bus fields.

  The bus_id is an ASCII string that contains the device's address on
  the bus. The format of this string is bus-specific. This is
  necessary for representing devices in sysfs. 

  parent is the physical parent of the device. It is important that
  the bus driver sets this field correctly. 

  The driver model maintains an ordered list of devices that it uses
  for power management. This list must be in order to guarantee that
  devices are shutdown before their physical parents, and vice versa.
  The order of this list is determined by the parent of registered
  devices.

  Also, the location of the device's sysfs directory depends on a
  device's parent. sysfs exports a directory structure that mirrors 
  the device hierarchy. Accurately setting the parent guarantees that
  sysfs will accurately represent the hierarchy.

  The device's bus field is a pointer to the bus type the device
  belongs to. This should be set to the bus_type that was declared
  and initialized before. 

  Optionally, the bus driver may set the device's name and release
  fields.

  The name field is an ASCII string describing the device, like

     "ATI Technologies Inc Radeon QD"

  The release field is a callback that the driver model core calls 
  when the device has been removed, and all references to it have 
  been released. More on this in a moment.

- Register the device. 

  Once the generic device has been initialized, it can be registered
  with the driver model core by doing:

       device_register(&dev->dev);

  It can later be unregistered by doing: 

       device_unregister(&dev->dev);

  This should happen on buses that support hotpluggable devices. 
  If a bus driver unregisters a device, it should not immediately free
  it. It should instead wait for the driver model core to call the 
  device's release method, then free the bus-specific object. 
  (There may be other code that is currently referencing the device
  structure, and it would be rude to free the device while that is 
  happening).

  
  When the device is registered, a directory in sysfs is created. 
  The PCI tree in sysfs looks like: 
  
  /sys/devices/pci0/
|-- 00:00.0
|-- 00:01.0
|   `-- 01:00.0
|-- 00:02.0
|   `-- 02:1f.0
|       `-- 03:00.0
|-- 00:1e.0
|   `-- 04:04.0
|-- 00:1f.0
|-- 00:1f.1
|   |-- ide0
|   |   |-- 0.0
|   |   `-- 0.1
|   `-- ide1
|       `-- 1.0
|-- 00:1f.2
|-- 00:1f.3
`-- 00:1f.5

  Also, symlinks are created in the bus's 'devices' directory
  that point to the device's directory in the physical hierarchy. 

/sys/bus/pci/devices/
|-- 00:00.0 -> ../../../devices/pci0/00:00.0
|-- 00:01.0 -> ../../../devices/pci0/00:01.0
|-- 00:02.0 -> ../../../devices/pci0/00:02.0
|-- 00:1e.0 -> ../../../devices/pci0/00:1e.0
|-- 00:1f.0 -> ../../../devices/pci0/00:1f.0
|-- 00:1f.1 -> ../../../devices/pci0/00:1f.1
|-- 00:1f.2 -> ../../../devices/pci0/00:1f.2
|-- 00:1f.3 -> ../../../devices/pci0/00:1f.3
|-- 00:1f.5 -> ../../../devices/pci0/00:1f.5
|-- 01:00.0 -> ../../../devices/pci0/00:01.0/01:00.0
|-- 02:1f.0 -> ../../../devices/pci0/00:02.0/02:1f.0
|-- 03:00.0 -> ../../../devices/pci0/00:02.0/02:1f.0/03:00.0
`-- 04:04.0 -> ../../../devices/pci0/00:1e.0/04:04.0

第二步:注册设备。
设备的结构体struct device代表一个单一的设备。它主要包含该设备和其他实体之间关系的描述的一些数据。

- 在bus-specific的设备类型中嵌入一个设备结构体

struct pci_dev {
       ...
       struct  device  dev;            /* Generic device interface */
       ...
};

  建议不要将通用设备设置为结构体的第一项以防止程序员盲目进行对象类型间的转换。
  相反,应该多建立宏或内联函数,用以通用对象类型转换。
#define to_pci_dev(n) container_of(n, struct pci_dev, dev)

static inline struct pci_dev * to_pci_dev(struct kobject * kobj)
{
return container_of(n, struct pci_dev, dev);
}

  这使得编译器很够验证所执行操作的类安全性(这是很好的)。

  - 初始化设备登记。

  当设备被发现或注册为总线类型时,
  总线驱动应当初始化通用设备。最重要的是要
  初始化bus_id,parent,和bus field。

  bus_id是总线上的一串包含着设备地址的ASCII字符串。
  字符串的格式是bus-specific,以此与sysfs中的设备相对应。
  
  parent表明它是从何继承而来,总线驱动程序正确地设置这个字段是非常重要的。
  
  驱动程序模型管理着一个用于电源管理的有序列表。
  该列表用以保证设备关闭早于他所继承的上级设备,反之亦然。
  该列表的顺序按照继承的先后关系排列。
  
  此外,该设备的sysfs目录的位置取决于上级设备。
  sysfs中导出的目录结构,反映设备层次。
  准确地设置的继承关系以保证,sysfs所代表层次的准确性。
  
  设备的bus field是一个指向该设备类型的指针,该设备类型必须经过申明与初始化。
  
  有时,总线驱动会定义设备名和释放。
  
  设备名是一串描述设备的ASCII字符串,例如:
"ATI Technologies Inc Radeon QD"

  释放驱动程序模型的核心是一个回调。
  当设备已被移除,所有对它的引用被释放。

- 注册设备。

  一旦通用设备被初始化,它可以被注册为驱动模型核心:
  
       device_register(&dev->dev);
  
  之后通过以下方式注销:

       device_unregister(&dev->dev);

  生效于支持热插拔设备的总线。
  如果总线驱动程序注销一个设备,它不应该立即释放
  。而应等待驱动程序模型的核心调用
  设备的释放方法,然后释放总线特定的对象。
  (可能有其他的代码目前引用该设备,当这种情况发生时,将是一次粗鲁而释放设备)。

  当设备被注册后,sysfs中的目录被创建。
  sysfs中的PCI树如下:
  
/sys/devices/pci0/
|-- 00:00.0
|-- 00:01.0
|   `-- 01:00.0
|-- 00:02.0
|   `-- 02:1f.0
|       `-- 03:00.0
|-- 00:1e.0
|   `-- 04:04.0
|-- 00:1f.0
|-- 00:1f.1
|   |-- ide0
|   |   |-- 0.0
|   |   `-- 0.1
|   `-- ide1
|       `-- 1.0
|-- 00:1f.2
|-- 00:1f.3
`-- 00:1f.5

  此外,总线的设备的目录中创建符号链接指向该设备的目录的物理层次结构中。

/sys/bus/pci/devices/
|-- 00:00.0 -> ../../../devices/pci0/00:00.0
|-- 00:01.0 -> ../../../devices/pci0/00:01.0
|-- 00:02.0 -> ../../../devices/pci0/00:02.0
|-- 00:1e.0 -> ../../../devices/pci0/00:1e.0
|-- 00:1f.0 -> ../../../devices/pci0/00:1f.0
|-- 00:1f.1 -> ../../../devices/pci0/00:1f.1
|-- 00:1f.2 -> ../../../devices/pci0/00:1f.2
|-- 00:1f.3 -> ../../../devices/pci0/00:1f.3
|-- 00:1f.5 -> ../../../devices/pci0/00:1f.5
|-- 01:00.0 -> ../../../devices/pci0/00:01.0/01:00.0
|-- 02:1f.0 -> ../../../devices/pci0/00:02.0/02:1f.0
|-- 03:00.0 -> ../../../devices/pci0/00:02.0/02:1f.0/03:00.0
`-- 04:04.0 -> ../../../devices/pci0/00:1e.0/04:04.0
Step 3: Registering Drivers.

struct device_driver is a simple driver structure that contains a set
of operations that the driver model core may call. 

- Embed a struct device_driver in the bus-specific driver. 

  Just like with devices, do something like:

struct pci_driver {
       ...
       struct device_driver    driver;
};

- Initialize the generic driver structure. 

  When the driver registers with the bus (e.g. doing pci_register_driver()),
  initialize the necessary fields of the driver: the name and bus
  fields. 

- Register the driver.

  After the generic driver has been initialized, call

driver_register(&drv->driver);

  to register the driver with the core.

  When the driver is unregistered from the bus, unregister it from the
  core by doing:

        driver_unregister(&drv->driver);

  Note that this will block until all references to the driver have
  gone away. Normally, there will not be any.

- Sysfs representation.

  Drivers are exported via sysfs in their bus's 'driver's directory. 
  For example:

/sys/bus/pci/drivers/
|-- 3c59x
|-- Ensoniq AudioPCI
|-- agpgart-amdk7
|-- e100
`-- serial

第三部:注册驱动。

struct device_driver是一个简单的驱动结构体包含一组调用驱动模型核心的操作

- 在bus-specific驱动中嵌入device_driver结构

  就像设备一样,如下:
  
struct pci_driver {
       ...
       struct device_driver    driver;
};

- 初始化通用驱动程序结构。

  当驱动程序注册的总线(例如做pci_register_driver的()),
  初始化必要的驱动程序领域的名称和巴士
  字段。

- 注册驱动程序。

  在通用驱动初始化之后,调用:
  
driver_register(&drv->driver);

  在内核中注册驱动。
  
  当驱动从总线中注销时,注销从内核开始,调用:
  
     driver_unregister(&drv->driver);
  
  这个过程将不会停止,直到所有驱动的关联都结束。通常都会有关联。

- sysfs表示。

  驱动程序通过sysfs输出总线的驱动程序的目录。
  例如:

/sys/bus/pci/drivers/
|-- 3c59x
|-- Ensoniq AudioPCI
|-- agpgart-amdk7
|-- e100
`-- serial

Step 4: Define Generic Methods for Drivers.

struct device_driver defines a set of operations that the driver model
core calls. Most of these operations are probably similar to
operations the bus already defines for drivers, but taking different
parameters. 

It would be difficult and tedious to force every driver on a bus to
simultaneously convert their drivers to generic format. Instead, the
bus driver should define single instances of the generic methods that
forward call to the bus-specific drivers. For instance: 

static int pci_device_remove(struct device * dev)
{
        struct pci_dev * pci_dev = to_pci_dev(dev);
        struct pci_driver * drv = pci_dev->driver;

        if (drv) {
                if (drv->remove)
                        drv->remove(pci_dev);
                pci_dev->driver = NULL;
        }
        return 0;
}

The generic driver should be initialized with these methods before it
is registered. 

        /* initialize common driver fields */
        drv->driver.name = drv->name;
        drv->driver.bus = &pci_bus_type;
        drv->driver.probe = pci_device_probe;
        drv->driver.resume = pci_device_resume;
        drv->driver.suspend = pci_device_suspend;
        drv->driver.remove = pci_device_remove;

        /* register with core */
        driver_register(&drv->driver);

Ideally, the bus should only initialize the fields if they are not
already set. This allows the drivers to implement their own generic
methods. 
第四步:定义驱动通用模式。

结构device_driver定义一组操作的驱动程序模型核心调用。
这些操作可能是类似操作总线已经定义驱动程序,但采取不同的
参数。

迫使每一个总线上的驱动程序转换成通用格式将是困难和繁琐的。
因此,总线驱动应该定义的单个实例的通用方法调用总线特定的驱动程序。例如:
static int pci_device_remove(struct device * dev)
{
        struct pci_dev * pci_dev = to_pci_dev(dev);
        struct pci_driver * drv = pci_dev->driver;

        if (drv) {
                if (drv->remove)
                        drv->remove(pci_dev);
                pci_dev->driver = NULL;
        }
        return 0;
}

这些通用驱动程序必须在注册前经过初始化。

        /* initialize common driver fields */
        drv->driver.name = drv->name;
        drv->driver.bus = &pci_bus_type;
        drv->driver.probe = pci_device_probe;
        drv->driver.resume = pci_device_resume;
        drv->driver.suspend = pci_device_suspend;
        drv->driver.remove = pci_device_remove;

        /* register with core */
        driver_register(&drv->driver);

理想的情况下,总线初始化字段只在他们未曾被设置时。
这使得驱动程序来实现自己的通用的方法。

Step 5: Support generic driver binding. 

The model assumes that a device or driver can be dynamically
registered with the bus at any time. When registration happens,
devices must be bound to a driver, or drivers must be bound to all
devices that it supports. 

A driver typically contains a list of device IDs that it supports. The
bus driver compares these IDs to the IDs of devices registered with it. 
The format of the device IDs, and the semantics for comparing them are
bus-specific, so the generic model does attempt to generalize them. 

Instead, a bus may supply a method in struct bus_type that does the
comparison: 

  int (*match)(struct device * dev, struct device_driver * drv);

match should return '1' if the driver supports the device, and '0'
otherwise. 

When a device is registered, the bus's list of drivers is iterated
over. bus->match() is called for each one until a match is found. 

When a device is successfully bound to a driver, device->driver is
set, the device is added to a per-driver list of devices, and a
symlink is created in the driver's sysfs directory that points to the
device's physical directory:

/sys/bus/pci/drivers/
|-- 3c59x
|   `-- 00:0b.0 -> ../../../../devices/pci0/00:0b.0
|-- Ensoniq AudioPCI
|-- agpgart-amdk7
|   `-- 00:00.0 -> ../../../../devices/pci0/00:00.0
|-- e100
|   `-- 00:0c.0 -> ../../../../devices/pci0/00:0c.0
`-- serial

This driver binding should replace the existing driver binding
mechanism the bus currently uses. 

第五步:支持通用驱动程序绑定。

该模型假设,设备或驱动程序可以动态地在任何时间注册到总线。
当注册行为发生时,设备必须绑定到驱动程序,或者驱动程序必须绑定到所有它支持的设备。

驱动程序通常包含一个它支持的设备ID列表。
总线驱动比较这些ID与注册的设备的ID。
设备ID的格式是总线特定的,所以通用的模型不会尝试去概括他们。

相应的,总线可以提供bus_type结构中的方法用于比较:

  int (*match)(struct device * dev, struct device_driver * drv);

如果驱动支持设备则返回“1”,否则返回“0”。

当一个设备被注册,总线的驱动程序列表迭代调用。
bus->match()被调用于每一个表中值,直到找到一个匹配项。

当一个设备被成功绑定到驱动程序,device->driver
设置,该设备被添加到每个驱动器的设备列表,
驱动的sysfs目录中创建符号链接指向设备的物理目录:

/sys/bus/pci/drivers/
|-- 3c59x
|   `-- 00:0b.0 -> ../../../../devices/pci0/00:0b.0
|-- Ensoniq AudioPCI
|-- agpgart-amdk7
|   `-- 00:00.0 -> ../../../../devices/pci0/00:00.0
|-- e100
|   `-- 00:0c.0 -> ../../../../devices/pci0/00:0c.0
`-- serial

该驱动程序绑定替换现有的驱动程序使用的绑定机制总线。

Step 6: Supply a hotplug callback.

Whenever a device is registered with the driver model core, the
userspace program /sbin/hotplug is called to notify userspace. 
Users can define actions to perform when a device is inserted or
removed. 

The driver model core passes several arguments to userspace via
environment variables, including

- ACTION: set to 'add' or 'remove'
- DEVPATH: set to the device's physical path in sysfs. 

A bus driver may also supply additional parameters for userspace to
consume. To do this, a bus must implement the 'hotplug' method in
struct bus_type:

     int (*hotplug) (struct device *dev, char **envp, 
                     int num_envp, char *buffer, int buffer_size);

This is called immediately before /sbin/hotplug is executed. 

第六步:提供热插拔回调。

每当一个设备被注册到驱动程序模型的核心,
用户空间程序/sbin/hotplug将会被调用以通报用户空间。
当一个设备被插入或删除时,用户可以定义要执行的操作。

驱动程序模型的核心通过几个参数传递给用户空间
环境变量,包括

- ACTION: set to 'add' or 'remove'
- DEVPATH: set to the device's physical path in sysfs. 

总钱驱动还可以提供额外的参数供用户空间使用。
要做到这一点,总线必须在struct bus_type中提供“热插拔”的方法。

Step 7: Cleaning up the bus driver.

The generic bus, device, and driver structures provide several fields
that can replace those defined privately to the bus driver. 

- Device list.

struct bus_type contains a list of all devices registered with the bus
type. This includes all devices on all instances of that bus type.
An internal list that the bus uses may be removed, in favor of using
this one.

The core provides an iterator to access these devices. 

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

- Driver list.

struct bus_type also contains a list of all drivers registered with
it. An internal list of drivers that the bus driver maintains may 
be removed in favor of using the generic one. 

The drivers may be iterated over, like devices: 

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

Please see drivers/base/bus.c for more information.

- rwsem 

struct bus_type contains an rwsem that protects all core accesses to
the device and driver lists. This can be used by the bus driver
internally, and should be used when accessing the device or driver
lists the bus maintains. 

- Device and driver fields. 

Some of the fields in struct device and struct device_driver duplicate
fields in the bus-specific representations of these objects. Feel free
to remove the bus-specific ones and favor the generic ones. Note
though, that this will likely mean fixing up all the drivers that
reference the bus-specific fields (though those should all be 1-line
changes).

第七步:清理总线驱动。

通用总线,设备和驱动程序结构提供了几个字段
可以取代那些用private定义的总线驱动。

- 设备列表。

struct bus_type包含一个注册到总线的所有设备列表
类型。这包括所有设备,总线类型的所有实例。
可能会被删除,转而采用该总线使用一个内部列表。

驱动程序可能被遍历,例如:

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

参照drivers/base/bus.c获取更多信息。

-rwsem

struct bus_type包含rwsem的保护所有核心访问
设备和驱动程序列表。这可以用于通过总线驱动器
内部,访问时,应使用的设备或驱动程序
列出了总线维护。

- 设备和驱动字段。

struct device中的一些字段和struct device_driver中
的一些bus-specific重复总线特定的表示这些对象中的字段。
删除任意总线特定字段,可能会意味着固定了所有的驱动程序
引用总线特定的字段(虽然这些都应该是1-line的改变)。

抱歉!评论已关闭.