分类: 嵌入式
2014-09-16 23:38:50
{
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);
};
分配
设备驱动是静态分配的结构。尽管一个系统中驱动支持的设备是多种多样的,device_driver结构提供了一个通用的驱动结构(不是一个特定的设备实例)。
初始化
驱动必须初始化至少name和bus域。它也应当初始化devclass域(when it arrives),这样,它可以获得合适的内部联系。它还应该初始化尽可能多的回调函数,尽管它们都是可选的。
声明
综上所述,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,
};
大多数驱动不能被完全转化为新的模型,因为他们所属的总线有一个总线特有的数据结构,此结构中的一些总线特定的域不是普遍性的。
这通常是device ID结构。一个驱动通常定义一个它所支持的device IDs数组。这些结构的格式和比较设备IDs的语义完全是总线特定的。把它们定义为总线特有接口会牺牲类型安全性(type-safety),所以我们保留了总线特有结构可用。
总线指定的驱动包含了一个通用结构device_driver,如:
struct pci_driver
{
const struct pci_device_id *id_table;
struct device_driver driver;
};
一个包含了bus-specific域的定义如下(using the eepro100 driver again):
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);
驱动在启动时注册这个结构。对那些没有bus-specific 域 (i.e. don't have a bus-specific driver structure)的驱动来说,它们使用driver_register函数,并传递一个指针给它们的device_driver结构对象。
尽管如此,大多数的驱动有一个bus-specific结构并且需要使用如pci_driver_register之类的函数和总线一起注册。
驱动尽可能早的注册它们的结构是很重要的。device_driver结构对象初始化核心的一些域与注册一起进行,这包括引用计数和锁。这些域被假设是一直有效的,并且可以被设备模型核心和总线驱动使用。
总线驱动变迁
通过定义封装函数,向新模型的变迁变得比较容易。驱动可以完全忽略那些公用的结构且让总线封装填充这些域。对于那些回调,总线可以定义通用的回调(转而)去调用驱动的bus-specific回调。
这个解决方案作为临时的过渡性的。为了获取驱动的类信息,驱动无论如何需要被修改。由于将驱动程序转为新的模型可以减少结构复杂性和代码规模,推荐在类信息已添加的情况下转化它们。
访问
一旦对象被注册,它可以访问对象的通用域,如锁和设备列表。
int driver_for_each_dev(struct device_driver * drv, void * data, int (*callback)(struct device * dev, void * data));
设备域是一个已绑定在驱动上的所有设备的列表。LDM核提供了一个helper函数来操作一个驱动控制的所有设备。This helper locks the driver on each node access, and does proper reference counting on each device as it accesses it.(这个helper函数锁定驱动在每个节点上的访问,并且在它访问时,为每个设备设置正确的访问计数。)
Sysfs
当一个驱动注册时,在它的总线的目录下会创建一个sysfs目录。在这个目录里,驱动可以输出一个接口到用户空间来控制驱动在一个全局基点上的操作。e.g.驱动中的断点调试的信息输出。
此目录的一个未来的特性是“devices”目录。这个目录将包含到驱动所支持的设备目录的软连接。
回调
int (*probe) (struct device * dev);
probe()在任务上下文中调用,此时总线的读写信号量被锁,且驱动未被完全绑定到一个设备。驱动通常使用container_of()来将“dev”转换为一个总线特有(bus-specific)的类型,在probe()或是别的例程中。那个类型常常提供设备资源数据,如pci_dev.resource[] 或者platform_device.resources,用来增加入dev->platform_data,初始化驱动。
这个回调使一个driver-specific的逻辑绑定到一个给定的设备上。它包括验证存在的设备:是一个驱动可以处理的版本;驱动数据结构可以被分配和初始化;硬件可以被初始化。驱动常常使用dev_set_drvdata()保存一个指针到他们的状态。当驱动成功绑定到一个设备上时,probe()函数返回0,驱动模型代码完成它的绑定驱动到设备的部分。
一个驱动的probe()函数可能返回一个负的错误码值指示驱动不能绑定到此设备上,此情况下,它需要释放所有它申请的资源。
int (*remove) (struct device * dev);
调用remove从一个设备上去绑定一个驱动。它可能被调用当一个设备被从系统物理移除、驱动模型被卸载、在一个重启序列中、或是别的一些原因。
由驱动负责决定设备是否存在。它应该释放任何设备特有的资源;i.e.设备的driver_data域中的所有元素。
如果设备仍存在,它应该(quiesce静默,沉寂)使设备进入休眠模式,将它置于一个支持的低功耗状态。
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 *);