Linux内核设备模型(6)

译者:郭少悲
2009/12/09

原文:linux-2.6/Documentation/driver-model/driver.txt

设备驱动

struct device_driver {
        char                    * name;
        struct bus_type         * bus;

        struct completion    unloaded;
        struct kobject        kobj;
        list_t                  devices;

        struct module        *owner;

        int     (*probe)        (struct device * dev);
        int     (*remove)       (struct device * dev);

        int     (*suspend)      (struct device * dev, pm_message_t state);
        int     (*resume)       (struct device * dev);
};

分配
~~~~

设备驱动静态分配上述驱动结构。尽管一个系统里一个驱动支持多个设备,但是
struct device_driver代表了驱动在系统里作为一个整体(不是哪个特定设备的实例)。

初始化
~~~~~~

The driver must initialize at least the name and bus fields. It should
also initialize the devclass field (when it arrives), so it may obtain
the proper linkage internally. It should also initialize as many of
the callbacks as possible, though each is optional.
驱动必须至少初始化name和bus域。它也应当初始化devclass域,这样它可以在内部
获得合适的连接(linkage)。它也应当尽可能的初始化回调函数指针,尽管每个回调函数指针
都是可选的。

声明
~~~~

如上所述,struct device_driver结构是静态分配的。下面是eepro100驱动的声明
实例。这个声明仅仅是假设的;它依赖于转换到新模型的具体驱动。

static struct device_driver eepro100_driver = {
       .name        = "eepro100",
       .bus        = &pci_bus_type,
      
       .probe        = eepro100_probe,
       .remove        = eepro100_remove,
       .suspend        = eepro100_suspend,
       .resume        = eepro100_resume,
};

绝大多数驱动将不会完全转换到新的设备模型,因为它们所属的总线有一个总线
相关的数据结构,而其中总线相关的域无法通用化。

一个最常见的例子就是设备ID结构。一个驱动会定义一个它所支持的设备ID集数组。
比较设备ID的数据结构和语法是完全总线相关的。将它们定义为总线相关的实体
会牺牲类型安全,因此我们继续将它们放置于总线相关的数据结构里。

总线相关的驱动应当在其驱动数据结构里包含一个通用的
struct device_driver域,像这样:

struct pci_driver {
       const struct pci_device_id *id_table;
       struct device_driver      driver;
};

一个包含总线相关的域的定义像这样:

static struct pci_driver eepro100_driver = {
       .id_table       = eepro100_pci_tbl,
       .driver           = {
        .name        = "eepro100",
        .bus        = &pci_bus_type,
        .probe        = eepro100_probe,
        .remove        = eepro100_remove,
        .suspend    = eepro100_suspend,
        .resume        = eepro100_resume,
       },
};

也许会发现嵌套式的数据结构初始化语法有点笨拙或者丑陋。然而迄今为止,它是我们
发现的最好的方式来初始化整个数据结构。

注册
~~~~

int driver_register(struct device_driver * drv);

驱动在一开始就注册其数据结构。对于没有总线相关的域的驱动(例如,不必有
总线相关的驱动结构),它们使用driver_register(),并向其传递一个指向它们
的struct device_driver对象的指针。

然而,绝大多数驱动将会有总线相关的数据结构,并需要将其注册到总线上,
例如像使用pci_driver_register()函数。

驱动注册它们的驱动结构越早越好。在向核心的注册过程中,会初始化struct
device_driver对象的若干个域,包括引用计数和锁。这些域被认为一直有效,
由设备模型核心和总线驱动来使用。


迁移总线驱动
~~~~~~~~~~~

通过定义封装函数接口,迁移到新的驱动模型变得比较容易。驱动可以忽略通用
结构,让总线封装函数来填充这些域。对于回调函数,总线定义通用的回调函数,
让总线相关的驱动里的回调函数来调用这些通用回调函数。

这种解决方案是暂时的。为了获得驱动里的类信息,驱动必须进行修改。为了将
驱动迁移到新模型,减少基础设施的复杂性以及代码量,建议在转换过程中
添加类信息。

访问
~~~~

一旦驱动对象被注册,对象的一些通用的域就能够被访问,比如锁和设备链表。

int driver_for_each_dev(struct device_driver * drv, void * data,
                int (*callback)(struct device * dev, void * data));

device域是一个设备链表,记录了绑定到驱动上的所有设备。内核驱动模提供了一个帮助
函数来操作这些设备。帮助函数在每次调用时首先获取驱动的锁,在访问每个设备时会增加合
适的引用计数。


sysfs
~~~~~

当一个驱动注册完成后,就会在/sys/bus/'所属总线'目录下创建一个相应的驱动目录。在
这个目录里,驱动向用户导出控制驱动操作的接口,例如,通过接口设置驱动
可以输出调试信息。

在将来的feature里,这个目录里会有一个设备目录。这个目录里包含了驱动支持的
所有的设备的符号连接。



回调函数
~~~~~~~~

    int    (*probe)    (struct device * dev);

probe()函数接口在任务上下文调用:总线获得resem锁,驱动绑定到一个设备时。
在prob()和其他回调函数里,驱动使用container_of(),将"dev"转换到一个总线
相关的结构。这个总线相关的结构通常提供设备资源数据,诸如pci_dev.resource[]
或者platform_device.resources,这些数据和dev->platform_data用于初始化
驱动。

probe()回调函数持有驱动相关的逻辑:如何将驱动绑定到一个给定的设备。这包含
验证设备是否存在,它的版本是否能够被驱动处理,驱动的数据结构能否被分配和初始
化,硬件是否能够被初始化。驱动通常会通过dev_set_drvdata()保存一个指向其
状态的指针。当驱动与一个设备成功的绑定后,probe()返回值为0,驱动模型会完成
其余的绑定工作。

一个驱动的probe()也许会返回一个负的errno值来说明这个驱动无法和设备绑定,
这种情况下,驱动应当释放掉它已分配的所有的资源。

    int     (*remove)    (struct device * dev);

remove()用于解除一个驱动和一个设备的绑定。这个回调函数会在如下情况
调用:在一个设备从系统中物理地移除时;或者在一个驱动模块卸载时;
或者在重启时;或者其他一些case中。

由驱动决定设备是否存在。它应当能够释放为设备分配的任何资源。例如,
释放设备的driver_data域所指的任何资源。

如果设备存在且不出于工作状态,它应当让设备处于低功耗状态。

    int    (*suspend)    (struct device * dev, pm_message_t state);

suspend()回调函数用于设置设备到低功耗状态。

    int    (*resume)    (struct device * dev);

resume()回调函数用于将设备从低功耗状态唤醒。


属性
~~~~
struct driver_attribute {
        struct attribute        attr;
        ssize_t (*show)(struct device_driver *driver, char *buf);
        ssize_t (*store)(struct device_driver *, const char * buf, size_t count);
};

设备驱动通过sysfs目录导出一些属性。
驱动可以通过使用DRIVER_ATTR宏定义自己的属性,其工作方式于DEVICE_ATTR类似。

示例:

DRIVER_ATTR(debug,0644,show_debug,store_debug);

等价于如下定义:

struct driver_attribute driver_attr_debug;

下面的接口函数用于添加/删除驱动目录里的属性:

int driver_create_file(struct device_driver *, struct driver_attribute *);
void driver_remove_file(struct device_driver *, struct driver_attribute *);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值