Documentation/driver-model/porting.txt

Chinese translated version of Documentation/Porting Drivers to the New Driver Model


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: 赵晶  anana53@qq.com
---------------------------------------------------------------------
Documentation/Porting Drivers to the New Driver Model 的中文翻译


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


中文版维护者: 赵晶  anana53@qq.com
中文版翻译者: 赵晶  anana53@qq.com
中文版校译者: 赵晶  anana53@qq.com


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


Porting Drivers to the New Driver Model
Patrick Mochel
7 January 2003


将驱动程序移植到新的驱动程序模型
Patrick Mochel
2003年1月7日


Overview
Please refer to Documentation/driver-model/*.txt for definitions of
various driver types and concepts. 
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


概述
~~~~
驱动程序类型及概念的定义参考Documentation/driver-model/*.txt


将设备驱动程序移植到新的模型大部分是通过总线驱动层。
因为这样可以减少对内核驱动的负面影响,同时总线驱动也可以逐渐转变。
简单地说,驱动程序模型由一系列对象组成,这些对象可以被嵌入更大的、总线相关的对象中。
这些通用对象中的字段可以替代总线专用对象中的字段。
通用对象必须向驱动程序模型的核心注册。这样,它们将通过sysfs文件系统接口和外界通信。
我们可以用如下命令挂载sysfs。
     # mount -t sysfs sysfs /sys




The Process
Step 0: Read include/linux/device.h for object and function definitions. 
Step 1: Registering the bus driver. 
- Define a struct bus_type for the bus driver.
struct bus_type pci_bus_type = {
        .name           = "pci",
};


步骤:
第0步:对象与功能的定义参见include/linux/device.h
第1步:注册总线驱动
- 为总线相关驱动定义一个bus_type结构体。
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);
  The bus type may be unregistered (if the bus driver may be compiled
  as a module) by doing:
     bus_unregister(&pci_bus_type);


- 注册总线结构,这应该在总线驱动的初始化中完成。通常在module_init()中。
static int __init pci_driver_init(void)
{
        return bus_register(&pci_bus_type);
}
subsys_initcall(pci_driver_init);
如果总线驱动被编译成模块,则需要调用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


- 输出总线类型以便他用
其他结构体中可能需要使用这个总线结构,因此我们需要在头文件发中声明这个结构体。
例如在include/linux/pci.h中:
extern struct bus_type pci_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 */
       ...
};


第2步:注册设备
- 结构体device描述一个设备,它主要包含描述该设备与其他实体的关系的元数据。
- 将结构体device嵌入总线相关设备类型中
struct pci_dev {
        ...
        struct  device  dev;            /* 通用设备接口 */
        ...
};


  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).


建议不要将通用设备作为结构体中的第一个,这样可以防止编程人员混淆对象类型。同时我们需要定义一些宏定义,或者内联函数来转换对 象类型, 例如:


#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);
}
这样,编译器就可以对所执行操作进行类型检查。


- 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.


 初始化注册设备
当一种总线的设备注册是,我们需要初始化通用device结构体,
最重要的是初始化bus_id,parent,和bus域。
bus_id是包含设备地址的ASCII字符串,该字符串的格式为总线专用格式。
这对用sysfs来描述设备是有必要的。
Parent是这个设备的物理上的父设备,总线驱动必须正确设置这个域。
驱动模型中保存了电源管理的顺序,这样才能够保证在关闭上层设备之前
先关闭他的所有子设备。这个顺序就是用parent域决定的。
同时,parent也决定了设备在sysfs中的位置。Sysfs是用反映设备层次结构
的目录。正确设置parent才能保证sysfs中的层次结构是正确的。
设备的bus字段指明了该设备所属的总线类型的指针,应将其设置为之前声
明并初始化过的总线类型。
同时你也能选择设置name和release字段。
Name域是用来描述设备的。例如下面是一个name的值。
      "ATI Technologies Inc Radeon QD"
Release域是在驱动模型核心在移除设备时调用的一个回调函数。


- 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).


- 注册设备


初始化完了通用device结构体后,我们就能调用如下函数注册:
       device_register(&dev->dev);
 以后可以用如下函数注销: 
       device_unregister(&dev->dev);
这只能用于支持可热插拔设备的总线。如果总线驱动注销某一设备,
总线驱动不能立刻释放device结构体,而应该等驱动程序模型核心调
用设备release才能释放总线相关对象。(可能有其他代码正在使用设
备结构,所以不要释放设备。)


  When the device is registered, a directory in sysfs is created. 
  The PCI tree in sysfs looks like: 


  在设备注册后,我们将在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


  Also, symlinks are created in the bus's 'devices' directory
  that point to the device's directory in the physical hierarchy. 
  
同时将在总线的device目录中创建指向物理层次结构的符号连接:


/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;
};


第三步:注册驱动程序
结构体device_driver是一个简单的包含驱动模型内核可能调用的函数的结构体。
- 和设备一样,我们可以将一个通用的device_driver放在一个和总线相关的驱动中。
代码如下:
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.


- 初始化通用结构体(device_driver结构体). 
在驱动程序向总线注册时(例如调用pci_register_driver())时,
初始化device_driver的相关域:name和bus字段。


- 注册驱动程序
  在初始化完成后调用如下函数注册:
driver_register(&drv->driver);
在驱动程序从总线中注销时,调用如下函数来注销:
        driver_unregister(&drv->driver);
注意这个函数将阻塞,直到没用其他程序使用这个驱动为止。
通常是没有其他程序使用这个驱动的。


- 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


- Sysfs 结构.
驱动程序一般在sysfs中的drivers目录中显示。
例如:
/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;
}


第四步:定义驱动的通用方法.
结构体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;
}




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. 


理想情况下,总线驱动程序只应该初始化还没初始化的部分。这样允许驱动程序实现他们自己的方法。




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. 


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


驱动模型中默认认为设备或者驱动程序能够动态向总线注册。在发生注册时,
设备必须和驱动绑定,或者是驱动程序必须和他支持的所有设备绑定。


驱动程序中通常包含一个他之策的设备ID列表。总线驱动比较这个ID和
注册的设备ID,ID和比较ID的方法都是和总线相关的,因此总线驱动中应该有这种方法。




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 driver is registered, the bus's list of devices is iterated
over. bus->match() is called for each device that is not already
claimed by a driver. 


总线需要提供一种比较ID的函数,如下:
  int (*match)(struct device * dev, struct device_driver * drv);
如果匹配成功,返回1,否则返回0.
在设备注册时,总线中的驱动程序列表将被遍历,直到发现一个匹配的驱动程序。
在驱动程序注册时,总线上的设备列表将被遍历。所有没和驱动绑定的设备将被测试。


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. 


在设备和驱动程序成功绑定后,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:设置成add或者remove。
-DEVPATH:设置成sysfs中的设备的物理地址。


总线驱动程序也可以向用户空间提供其他的变量,为了达到这个目的,总线驱动必须
在bus_type中实现hotplug方法。


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


这个调用将在/sbin/hotplug执行之前调用。


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 *));


第七步:清理总线驱动程序。


通用的bus,device和driver结构体提供了能够替代总线驱动定义的域中的域。
- 设备列表
bus_type中包含了一个这个总线上注册的所有设备的列表。这包含了这个总线
种类中的所有驱动实例。总线使用的内部列表可以使用这个列表。
内核提供了一个访问这些设备的方法
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.


- 驱动列表
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 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).


bus_type中包含了一个保护所有对设备和驱动列表访问的信号量。
总线驱动程序能够使用这个信号量,在访问设备和驱动列表的时候需要考虑。


- 设备的驱动域


结构体device和结构体device_driver中有写总线相关的域是冗余的。
可以删除这些字段。但是要注意,这样需要修复这个总线上的所有的驱动程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值