www.ourkernel.com 我们的内核
这样,编译器就可以对所执行操作进行类型检查。
- 初始化注册设备 当一种总线的设备注册是,我们需要初始化通用device结构体,最重要的是初始化bus_id,parent,和bus域。
bus_id是包含设备地址的ASCII字符串,该字符串的格式为总线专用格式。这对用sysfs
来描述设备是有必要的。 Parent是这个设备的物理上的父设备,总线驱动必须正确设置这个域。
驱动模型中保存了电源管理的顺序,这样才能够保证在关闭上层设备之前先关闭他
的所有子设备。这个顺序就是用parent域决定的。
同时,parent也决定了设备在sysfs中的位置。Sysfs是用反映设备层次结构的目录。正确设置parent才能保证sysfs中的层次结构是正确的。
\TI Technologies Inc Radeon QD\ Release域是在驱动模型核心在移除设备时调用的一个回调函数。
- 注册设备
初始化完了通用device结构体后,我们就能调用如下函数注册:
device_register(&dev->dev);
以后可以用如下函数注销:
device_unregister(&dev->dev);
这只能用于支持可热插拔设备的总线。如果总线驱动注销某一设备,总线驱动不能立刻释放device结构体,而应该等驱动程序模型核心调用设备release才能释放总线相关对象。(可能有其他代码正在使用设备结构,所以不要释放设备。)
在设备注册后,我们将在sysfs中创建一个目录,PCI设备的目录如下:
/sys/devices/pci0/ |-- 00:00.0
36
设备的bus字段指明了该设备所属的总线类型的指针,应将其设置为之前声明并初始化
过的总线类型。
同时你也能选择设置name和release字段。
Name域是用来描述设备的。例如下面是一个name的值。
www.ourkernel.com 我们的内核
|-- 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
第三步:注册驱动程序
结构体device_driver是一个简单的包含驱动模型内核可能调用的函数的结构体。
- 和设备一样,我们可以将一个通用的device_driver放在一个和总线相关的驱动中。代码如
37
www.ourkernel.com 我们的内核
下:
struct pci_driver { ...
struct device_driver driver; };
- 初始化通用结构体(device_driver结构体).
在驱动程序向总线注册时(例如调用pci_register_driver())时,初始化device_driver的
相关域:name和bus字段。
- 注册驱动程序
在初始化完成后调用如下函数注册:
driver_register(&drv->driver);
在驱动程序从总线中注销时,调用如下函数来注销:
driver_unregister(&drv->driver); 注意这个函数将阻塞,直到没用其他程序使用这个驱动为止。通常是没有其他程序使用这个驱动的。
- Sysfs 结构.
Drivers are exported via sysfs in their bus's 'driver's directory. For example:
驱动程序一般在sysfs中的drivers目录中显示。 例如:
/sys/bus/pci/drivers/ |-- 3c59x
|-- Ensoniq AudioPCI |-- agpgart-amdk7 |-- e100 `-- serial
第四步:定义驱动的通用方法.
38
www.ourkernel.com 我们的内核
结构体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; }
在主持前,我们应该用如下方法初始化通用的驱动。
/* 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);
理想情况下,总线驱动程序只应该初始化还没初始化的部分。这样允许驱动程序实现他们自己的方法。
第五步:支持通用驱动绑定
驱动模型中默认认为设备或者驱动程序能够动态向总线注册。在发生注册时,设备必须和驱动绑定,或者是驱动程序必须和他支持的所有设备绑定。
驱动程序中通常包含一个他之策的设备ID列表。总线驱动比较这个ID和注册的设备ID,
39
www.ourkernel.com 我们的内核
ID和比较ID的方法都是和总线相关的,因此总线驱动中应该有这种方法。
总线需要提供一种比较ID的函数,如下:
int (*match)(struct device * dev, struct device_driver * drv);
如果匹配成功,返回1,否则返回0.
在设备注册是,总线中的驱动程序列表将被遍历,直到发现一个匹配的驱动程序。
在驱动程序注册时,总线上的设备列表将被遍历。所有没和驱动绑定的设备将被测试。
在设备和驱动程序成功绑定后,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
这种机制应该替代现在的绑定机制。
第六步:提供一个热插拔调用
在设备向驱动模型核心注册时,内核将调用/sbin/hotplug中的程序来通知用户空间。用户能够在这里定义设备插入和拔出时的动作。
内核驱动通过一些环境变量给用户空间传递信息。
-ACTION:设置成add或者remove。
-DEVPATH:设置成sysfs中的设备的物理地址。
总线驱动程序也可以向用户空间提供其他的变量,为了达到这个目的,总线驱动必须在bus_type中实现hotplug方法。
40