如上所述,structdevice_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的语义完全是特定于总线的。将它们定义为特定于总线的实体会牺牲类型安全性,因而我们保留特定于总线的结构。
特定于总线的驱动程序应当在特定于总线的驱动程序的定义中包含一个通用的structdevice_driver。像这样:
struct pci_driver {
const struct pci_device_id *id_table;
struct device_driver driver;
};
包含总线特定数组的定义如下所示(再度使用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,
},
};
有些人可能会发觉嵌入式结构初始化的句型笨拙甚至有点难看。到目前为止linux 版本,这是我们找到的最好的方式来做我们想做的事……
注册
int driver_register(struct device_driver *drv);
驱动程序在启动时注册该结构。对于没有总线特定数组的驱动程序(即没有总线特定的驱动程序结构),它们将使用driver_register并将表针传递给它们的structdevice_driver对象。
但是,大多数驱动程序将具有特定于总线的结构,而且须要使用例如pci_driver_register之类的东西向总线注册。
驱动程序提早注册它们的驱动程序结构很重要。向内核注册会初始化structdevice_driver对象中的几个数组,包括引用计数和锁。假设这种数组仍然有效,而且可以由设备模型核心或总线驱动程序使用。
过渡总线驱动程序
通过定义包装函数,可以更轻松地过渡到新模型。驱动程序可以完全忽视通用结构linux驱动程序开发视频,让总线包装器填充数组。对于反弹,总线可以定义通用反弹,将调用转发给驱动程序的特定于总线的反弹。
此解决方案只是暂时的。为了在驱动程序中获取类信息,无论怎样都必须更改驱动程序。因为将驱动程序转换为新模型应当会降低一些基础设施的复杂性和代码大小linux驱动程序开发视频,因而建议在添加类信息时进行转换。
访问
一旦对象被注册,它就可以访问对象的公共数组,例如锁和设备列表:
int driver_for_each_dev(struct device_driver *drv, void *data,
int (*callback)(struct device *dev, void *data));
devices数组是已绑定到驱动程序的所有设备的列表。LDM核心提供了一个辅助函数来操作驱动程序控制的所有设备。这个助手在每位节点访问时锁定驱动程序,并在每位设备访问它时对它进行适当的引用计数。
系统文件
注册驱动程序后,会在其总线目录中创建一个sysfs目录。在此目录中,驱动程序可以向用户空间导入插口,以在全局基础上控制驱动程序的操作;比如,在驱动程序中切换调试输出。
该目录的未来功能将是“devices”目录。该目录将包含指向它支持的设备目录的符号链接。
反弹
int (*probe) (struct device *dev);
probe()条目在任务上下文中调用,总线的rwsem被锁定而且驱动程序部份绑定到设备。驱动程序一般在probe()和其他类库中使用container_of()将“dev”转换为特定于总线的类型。该类型一般提供设备资源数据,比如pci_dev.resource[]或platform_device.resources,不仅dev->platform_data外,还用于初始化驱动程序。
此反弹包含特定于驱动程序的逻辑以将驱动程序绑定到给定设备。这包括验证设备是否存在,它是驱动程序可以处理的版本,可以分配和初始化驱动程序数据结构,以及可以初始化任何硬件。驱动程序一般使用dev_set_drvdata()储存指向其状态的表针。当驱动程序成功地将自己绑定到该设备时,probe()将返回零,但是驱动程序模型代码将完成其将驱动程序绑定到该设备的部份。
驱动程序的probe()可能会返回一个负的errno值,表示驱动程序没有绑定到这个设备,在这些情况下,它应当释放它分配的所有资源。
可选地,假若驱动程序依赖于尚不可用的资源(比如,由仍未初始化的驱动程序提供),则probe()可能会返回-EPROBE_DEFER。驱动核心会将设备装入延后侦测列表中,稍后将尝试再度调用它。假如驱动程序必须延后,它应当尽快返回-EPROBE_DEFER以降低花销在设置工作上的时间,这种工作将须要在之后展开并重新执行。
警告
-假如probe()早已创建了子设备,则不得返回EPROBE_DEFER,虽然这种子设备在清除路径中再度被删掉。假如在注册子设备后返回-EPROBE_DEFER,则可能会引致对同一驱动程序的.probe()调用无限循环。
void (*sync_state) (struct device *dev);
sync_state只为一个设备调用一次。当设备的所有消费者设备都已成功侦测时调用它。设备的消费者列表是通过查看将该设备联接到其消费者设备的设备链接获得的。
第一次尝试调用sync_state()是在late_initcall_sync()期间进行的,便于固件和驱动程序有时间将设备互相链接。在第一次尝试调用sync_state()期间,假如该时间点设备的所有消费者都已成功侦测,则立刻调用sync_state()。假如在第一次尝试期间没有设备的消费者,那也被觉得是“设备的所有消费者都已侦测”并且立刻调用sync_state()。
假如在第一次尝试调用设备的sync_state()期间,仍有消费者未成功侦测,则sync_state()调用将被延后,但是只有当设备的一个或多个消费者成功侦测时,就会在将来重新尝试。假如在重试期间,驱动核心发觉设备的一个或多个消费者仍未侦测,则再度延后sync_state()调用。
sync_state()的一个典型用例是让内核干净利落地从引导加载程序接管设备管理。诸如linux格式化硬盘,假如引导加载程序将设备保留在特定硬件配置上,则设备的驱动程序可能须要将设备保持在引导配置中,直至设备的所有消费者都已侦测到为止。一旦设备的所有消费者都进行了侦测,设备的驱动程序就可以同步设备的硬件状态,以匹配所有消费者恳求的聚合软件状态。因而得名sync_state()。
尽管可以从sync_state()中获益的资源的显著示例包括调节器等资源,但sync_state()也可用于IOMMU等复杂资源。诸如,具有多个消费者(其地址由IOMMU重新映射的设备)的IOMMU可能须要将其映射固定在(或附加到)引导配置,直至其所有消费者都已侦测。
尽管sync_state()的典型用例是让内核干净利落地从引导加载程序接管设备管理,但sync_state()的使用并不限于此。在设备的所有消费者都侦测过以后,只要有必要采取行动就可以使用它:
int (*remove) (struct device *dev);
调用remove来解除驱动程序与设备的绑定。假如设备从系统中数学移除,假如正在卸载驱动程序模块,在重新启动序列期间,或在其他情况下,可能会调用此技巧。
由驱动程序确定设备是否存在。它应当释放专门为设备分配的任何资源;即设备的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目录导入属性。驱动程序可以使用与DEVICE_ATTR_RW和DEVICE_ATTR_RO宏一样工作的DRIVER_ATTR_RW和DRIVER_ATTR_RO宏来申明属性。
事例:
DRIVER_ATTR_RW(debug);