Linux驱动模型 - 驱动程序

Device Drivers

 

struct device_driver {

char                  * name;

        structbus_type         * bus;

        structcompletion             unloaded;

        structkobject                    kobj;

        list_t                  devices;

        struct module                    *owner;

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

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

        int     (*suspend)      (struct device * dev, pm_message_tstate);

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

};

 

Allocation~~~~~~~~~~

device_driver结构是静态创建的. 系统中或许有多个被同一驱动支持的设备,这个结构代表的是驱动自身(而不是某个特定的设备).

 

Initialization~~~~~~~~~~~~~~

驱动必须至少初始化域name 和bus. 还应该要初始化域devclass (如果有的话), 这样才能获得正确的内部链接. 虽然对于callback的初始化是可选的,但多多益善。

 

Declaration~~~~~~~~~~~

如上所述, 结构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,

};

 

绝大多数的驱动无法被改造为完全符合新的驱动模型,这是因为驱动所属的bus有该bus特有的数据结构,该结构又包含有该bus特有的域,而这些特有的部分是无法被范化的。

最普遍的例子是device ID 结构. 驱动一般会定义一个device ID数组,用于存放该驱动所支持的设备. 该结构的格式及其语义完全是其所属bus所特有的. 将它们定义在bus的数据结构中会损害类型安全性,所以我们还是保留bus相关的数据结构。.

和特定bus相关的驱动声明中应该要包含与bus无关的device_driver结构及bus相关的结构. 就象这样:

struct pci_driver {

conststruct    pci_device_id           *id_table;

struct              device_driver          driver;

};

 

包含bus相关的驱动定义则看起来是这样的 (还是使用eepro100驱动):

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,

},

};

 

有人会觉得嵌套数据结构的初始化很难看,但这是截止目前为止我们能想到的最佳解决方案...

 

Registration~~~~~~~~~~~~

int driver_register(struct device_driver * drv);

 

驱动程序在启动的时候将上述结构注册到内核. 那些不包含特定总线的驱动使用函数driver_register()进行注册,参数为指向device_driver结构对象的指针.

然而绝大多数的驱动都包含有总线相关的数据结构,这种情形下就要使用诸如pci_driver_register()这样的函数进行注册.

驱动程序尽可能早地注册至关重要. 注册时内核会对device_driver对象的几个域进行初始化,包括reference count 和lock. 这些域被认为是可以随时可以访问的,设备模型核心和总线驱动都有可能要访问它们。.

 

Transition BusDrivers ~~~~~~~~~~~~~~~~~~~~~~

通过定义一些wrapper函数, 向新模型的转变会更容易一些. 驱动程序可以选择忽略整个device_driver结构而让bus的wrapper函数填充各个域. 对结构中的那些callback函数来说,bus会定义通用的callback,通用callback会将调用转到驱动中的与bus相关的那些callback去.

这是个暂时方案. 为了从驱动中获取class信息,现有的驱动无论润和都要被修改的. 将驱动程序更新到新的驱动模型会减少底层的复杂度和代码量,因此更新时推荐加入对class信息的支持.

 

Access ~~~~~~

对象一旦被注册,就可以对其中的一些普通域进行访问,比如lock 和设备列表.

int driver_for_each_dev(struct device_driver * drv,

void* data,

int(*callback)(struct device * dev, void * data)

);

 

Devices域是绑定到该驱动的所有设备的列表. LDM核心提供一个辅助函数用以对驱动程序控制的所有设备进行操作. 每当有节点被访问时,该辅助函数都会锁住驱动并维护每一个设备的reference count.

 

sysfs ~~~~~

驱动注册后, 内核会在/sysfs中的相应总线类型下创建目录. 这个目录是驱动提供给用户空间的接口,用以控制对驱动的操作。比如,打开/关闭驱动的调试信息开关.

未来/sysfs下还会支持'devices'目录. 这个目录下会包含指向设备目录的符号链接.

 

Callbacks~~~~~~~~~

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

probe()在任务(task)上下文中被调用, 此时,bus的读写信号量(rwsem)是锁住的,而驱动只是部分地绑定到设备.  在probe()和其它函数中,驱动程序一般使用宏container_of()将"dev"转变为设备相关的类型.  这个数据类型通常提供设备的资源信息,比如pci_dev.resource[]或platform_device.resources, 这些信息连同dev->platform_data一起被用来初始化驱动程序.

这个callback函数包含有驱动相关的逻辑用以将驱动绑定到设备. 包括:

确认设备存在;

设备的版本号是驱动所支持的;

驱动自身的数据结构可以申请到内存并正确初始化。

驱动通常在dev_set_drvdata()中保存一个指向驱动状态的指针. 驱动和设备绑定成功的情况下,probe()返回零,内核中驱动模型的代码会继续完成绑定的工作. 否则,返回错误码(负数)指示绑定没有成功。这种情形下要释放所有已申请的资源.

 

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

remove()用于解除绑定. 这个回调函数可能在以下情形被调用:

物理设备自系统中移除;

驱动模块被移除(rmmod);

系统重启;

其它.

判断设备是否存在的工作由驱动程序完成. 如果设备被移除,驱动要释放为该设备申请的所有资源-也就是设备的driver_data结构中的所有域.

如果设备还在,驱动则要将设备置于低功耗状态。

 

int    (*suspend)       (struct device * dev, pm_message_tstate);

suspend用于将设备置于低功耗状态.

 

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

Resume 用于将设备从低功耗状态转回正常工作状态.

 

Attributes~~~~~~~~~~

 structdriver_attribute {

structattribute        attr;

        ssize_t(*show)(struct device_driver *, char * buf, size_t count, loff_t off);

ssize_t(*store)(struct device_driver *, const char * buf, size_t count, loff_t off);

};

 

驱动可以透过它在/sysfs中的目录向外界声明自身的属性. 用宏DRIVER_ATTR 定义属性。顺便说一句,宏DEVICE_ATTR的工作方式是一样的.

 

Example:

DRIVER_ATTR(debug,0644,show_debug,store_debug);

 

上面这个宏等同于:

struct driver_attribute driver_attr_debug;

这个数据结构可进一步用来增加/移除属性:

int driver_create_file(struct device_driver *, structdriver_attribute *);

void driver_remove_file(struct device_driver *, structdriver_attribute *);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值