设备驱动开发是Linux开发领域一个非常重要的部分,在Linux源码的85%都是驱动程序的代码。设备驱动开发不仅需要了解硬件底层的知识,还需要拥有操作系统的背景。驱动程序追求的是高效,稳定,驱动程序发生的问题有可能直接导致整个系统的崩溃。
驱动程序不主动运行,意味着驱动程序是等待应用程序来调用的。应用程序通过内核来调用驱动程序,实现与实际硬件设备的通信。Linux外设通常可以分为字符型设备、块设备、网络设备三种类型。
设备驱动开发很复杂,好的驱动不好写,比如一个驱动程序被多个进程使用,这就需要考虑到并发和竞态的问题。
驱动程序可以在编译内核时静态编译到内核当中,也可以通过模块化的方式在需要时进行加载。驱动程序在模块化编译完成后会以.ko作为扩展名,我们可以使用insmod命令进行加载,使用rmmod命令进行卸载,可以使用lsmod命令查看当前运行的内核当中已经加载的模块。
1、在通常的驱动程序代码中,我们可以找到一下两个函数:
module_init(irfpa_module_init);
module_exit(irfpa_module_exit);
当我们使用insmod命令加载模块时,模块化的初始化函数会自动被调用,用来向内核注册驱动程序。系统使用module_init来标记初始化函数。当使用rmmod命令卸载模块时,模块的清除函数被调用。清除函数使用 module_exit来标记。
模块化的结构是Linux推荐的一种方式,其方便系统的精简和组织,这也是Linux很好的一个特性,实现了在运行时可扩展的目的。
**2、**sys文件系统简介
在2.6内核之前,绝大多数的驱动程序最终都被映射到/dev目录下,对于字符型驱动程序而言,采用file_operations类型的数据结构来组织驱动程序。这使得/dev目录变得越来越繁琐,对与普通用户而言,变得不具可读性。之后就采用sys文件系统来组织驱动。
sys文件系统是一个类似于proc文件系统的特殊文件系统,它由系统自动维护,用于将系统中的设备组织成层次结构,并向用户模式程序提供详细的内核数据结构信息。在sys目录下有:
block:包含所有的块设备
devices:包含系统所有的设备,并根据设备挂载的总线类型组织成层次结构。
bus:包含系统中所有的总线类型。
drivers:包含内核中所有已注册的设备驱动程序。
class:包含系统中的设备类型(我们自定义的设备类型就在此目录中)
对于用户程序而言,若要对设备进行操作,只需在sys目录下找到对应的文件,对其进行读写即可完成。
3、设备驱动程序的作用在于提供机制,而不是策略。简单的说驱动程序是实现“需要什么功能”(机制),而用户程序利用驱动程序“如何使用这些功能”(策略)来完成不同的任务。我觉得就是我们编出一个模块,用户程序调用这个模块。
在1、中两个宏帮助我们向内核标记了模块的初始化与清除函数。在驱动程序中主要的注册信息都在irfpa_module_init这个函数中完成了。命令insmod将自动调用这个函数。
/**注册设备配置,注册驱动platform_driver_register
* irfpa_module_init - register the Device Configuration.
* Returns 0 on success, otherwise negative error.
*/
static int __init irfpa_module_init(void)
{
return platform_driver_register(&irfpa_platform_driver);
}
从Linux2.6内核起,引入一套新的驱动管理和注册机制:platform_device 和 platform_driver 。Linux 中大部分的设备驱动,都可以使用这套机制,设备用 platform_device 表示;驱动用 platform_driver 进行注册。platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序中用使用这些资源时,通过platform device提供的标准接口进行申请并使用。
platform 是一个虚拟的地址总线,相比 PCI、USB,它主要用于描述SOC上的片上资源。platform 所描述的资源有一个共同点:在CPU 的总线上直接取址。平台设备会分到一个名称(用在驱动绑定中)以及一系列诸如地址和中断请求号(IRQ)之类的资源。
platform_device_系列函数,实际上是注册了一个叫platform的虚拟总线。使用约定是如果一个不属于任何总线的设备,例如蓝牙,串口等设备,都需要挂在这个虚拟总线上。
4、要用注册一个platform驱动的步骤:
1,注册设备platform_device_register
2,注册驱动platform_driver_register,过程中在系统寻找注册的设备(根据.name),找到后运行.probe进行初始化。
注册时候的两个名字必须一样,才能match上,才能work。
在设备树中,已经把FPGA实现的逻辑作为设备挂载在ARM处理器的操作系统中,这里就不用注册设备了,只需要注册驱动就好了。
irfpa: irfpa@43c00000 {
compatible = "sitp,irfpa-1.00.a";
reg = < 0x43c00000 0x100 0x43000000 0x100 0x18000000 0x8000000 0x40000000 0x800>; //irfpa vdma irfpa_buf irfpa_xinf
};
platform_driver_register(&irfpa_platform_driver) 会向系统注册irfpa_platform_driver这个驱动程序,这个函数会根据 irfpa_platform_driver中的”sitp,irfpa-1.00.a”内容,搜索系统注册的device中有没有这个platform_device,如果有,就会执行 platform_driver(也就是irfpa_platform_driver的类型)中的.probe函数。
注意:对只需要初始化运行一次的函数都加上__init属性,__init 宏告诉编译器如果这个模块被编译到内核则把这个函数放到(.init.text)段,module_exit的参数卸载时同__init类似,如果驱动被编译进内核,则__exit宏会忽略清理函数,因为编译进内核的模块不需要做清理工作,显然__init和__exit对动态加载的模块是无效的,只支持完全编译进内核。
这里实现的是驱动:
驱动注册中,需要实现的结构体是:platform_driver 。
static struct platform_driver irfpa_platform_driver = {
.probe = irfpa_drv_probe,
.remove = __devexit_p(irfpa_drv_remove),
.driver = {
.owner = THIS_MODULE,
.name = DRIVER_NAME,
.of_match_table = irfpa_of_match, //好像置为NULL了
},
}
#ifdef CONFIG_OF
static struct of_device_id irfpa_of_match[] __devinitdata = {
{ .compatible = "sitp,irfpa-1.00.a", },
{ }
};
MODULE_DEVICE_TABLE(of, irfpa_of_match);
#else
#define irfpa_of_match NULL
#endif /* CONFIG_OF */
在驱动程序的初始化函数中,调用了platform_driver_register()注册 platform_driver 。需要注意的是:platform_driver 和 platform_device 中的 name 变量的值必须是相同的 。这样在 platform_driver_register() 注册时,会将当前注册的 platform_driver 中的 name 变量的值和已注册的所有 platform_device 中的 name 变量的值进行比较,只有找到具有相同名称的 platform_device 才能注册成功。当注册成功时,会调用 platform_driver 结构元素 probe 函数指针。
platform_driver_register()的注册过程:
1 platform_driver_register(&irfpa_platform_driver)
2 driver_register(&irfpa_platform_driver->driver)
3 bus_add_driver(&irfpa_platform_driver)
4 driver_attach(&irfpa_platform_driver)
5 bus_for_each_dev(&irfpa_platform_driver->bus, NULL, &irfpa_platform_driver, __driver_attach)
6 __driver_attach(irfpa, &irfpa_platform_driver)
7 driver_probe_device(&irfpa_platform_driver, irfpa)
8 really_probe(irfpa, &irfpa_platform_driver)
在really_probe()中:为设备指派管理该设备的驱动:dev->driver = drv, 调用probe()函数初始化设备:drv->probe(dev)
这里真正调用了probe函数。
典型的Platform device是系统中的各种自主设备,包括各种桥接在外围总线上的port-based device和host,以及各种集成在SOC platform上的控制器。他们都有一个特点,那就是CPU BUS可以直接寻址,或者特殊的情况platform_device连接在其他总线上,但是它的寄存器是可以被直接寻址的。
Platform device有一个名字,用来进行driver的绑定;还有诸如中断,地址之类的一些资源列表
Probe()函数必须验证指定设备的硬件是否真的存在,probe()可以使用设备的资源,包括时钟,platform_data等
a、platform_driver_register(&&irfpa_platform_driver) //注册平台驱动
int platform_driver_register(struct platform_driver *drv)
{
。。。
return driver_register(&drv->driver);
}
b、driver_register(&drv->driver); //注册驱动到bus,@drv: 要注册的驱动
这个函数开始先判断bus->p是否为空,如果不为空然后判断驱动跟驱动的总线是否有冲突的函数注册,如果有冲突就给出警告信息,然后在注册在bus上的driver寻找是否有跟要注册的driver相同,有则表明驱动已被注册过,返回错误。经过上面的验证后,将驱动添加注册到bus上,如果没问题,则再将驱动添加到同一属性的组中,在sysfs下表现为同一个目录。
int driver_register(struct device_driver *drv)
{
int ret;
struct device_driver *other;
BUG_ON(!drv->bus->p);
if ((drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown))
printk(KERN_WARNING "Driver '%s' needs updating - please use "
"bus_type methods
", drv->name);
other = driver_find(drv->name, drv->bus);
if (other) {
put_driver(other);
printk(KERN_ERR "Error: Driver '%s' is already registered, "
"aborting...
", drv->name);
return -EBUSY;
}
ret = bus_add_driver(drv);
if (ret)
return ret;
ret = driver_add_groups(drv, drv->groups);
if (ret)
bus_remove_driver(drv);
return ret;
}
c、bus_add_driver(drv); //添加驱动到总线
int bus_add_driver(struct device_driver *drv)
{
struct bus_type *bus;
struct driver_private *priv;
int error = 0;
bus = bus_get(drv->bus);
if (!bus)
return -EINVAL;
pr_debug("bus: '%s': add driver %s
", bus->name, drv->name);
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
error = -ENOMEM;
goto out_put_bus;
}
klist_init(&priv->klist_devices, NULL, NULL);
priv->driver = drv;
drv->p = priv;
priv->kobj.kset = bus->p->drivers_kset;
error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
"%s", drv->name);
if (error)
goto out_unregister;
if (drv->bus->p->drivers_autoprobe) {
error = driver_attach(drv);
if (error)
goto out_unregister;
}
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
module_add_driver(drv->owner, drv);
error = driver_create_file(drv, &driver_attr_uevent);
if (error) {
printk(KERN_ERR "%s: uevent attr (%s) failed
",
__func__, drv->name);
}
error = driver_add_attrs(bus, drv);
if (error) {
printk(KERN_ERR "%s: driver_add_attrs(%s) failed
",
__func__, drv->name);
}
error = add_bind_files(drv);
if (error) {
printk(KERN_ERR "%s: add_bind_files(%s) failed
",
__func__, drv->name);
}
kobject_uevent(&priv->kobj, KOBJ_ADD);
return 0;
out_unregister:
kfree(drv->p);
drv->p = NULL;
kobject_put(&priv->kobj);
out_put_bus:
bus_put(bus);
return error;
}
d、driver_attach(drv);//为驱动寻找相应的设备、//该函数将调用bus_for_each_dev()。
int driver_attach(struct device_driver *drv)
{
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
e、bus_for_each_dev(drv->bus, NULL, drv, __driver_attach)
int bus_for_each_dev(struct bus_type *bus, struct device *start,
void *data, int (*fn)(struct device *, void *))
{
struct klist_iter i;
struct device *dev;
int error = 0;
if (!bus)
return -EINVAL;
klist_iter_init_node(&bus->p->klist_devices, &i,
(start ? &start->p->knode_bus : NULL));
while ((dev = next_device(&i)) && !error)
error = fn(dev, data);
klist_iter_exit(&i);
return error;
}
f、__driver_attach(struct device * dev, void * data) //dev 为使用驱动程式的设备结构体 //i2c总线根据设备client名字和id_table中的名字进行匹配的。如果匹配了,则返回id值,在i2c_device_match中则返回真。也就是bus的match函数将会返回真。那将会进入
driver_probe_device()。
static int __driver_attach(struct device *dev, void *data)
{
struct device_driver *drv = data;
* Lock device and try to bind to it. We drop the error
* here and always return 0, because we need to keep trying
* to bind to devices and some drivers will return an error
* simply if it didn't support the device.
*
* driver_probe_device() will spit a warning if there
* is an error.
*/
if (!driver_match_device(drv, dev))
return 0;
if (dev->parent) /* Needed for USB */
down(&dev->parent->sem);
down(&dev->sem);
if (!dev->driver)
driver_probe_device(drv, dev);
up(&dev->sem);
if (dev->parent)
up(&dev->parent->sem);
return 0;
}
g、driver_probe_device(drv, dev)
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
int ret = 0;
if (!device_is_registered(dev)) //首先判断这个device是否已经注册
return -ENODEV;
pr_debug("bus: '%s': %s: matched device %s with driver %s
",
drv->bus->name, __func__, dev_name(dev), drv->name);
ret = really_probe(dev, drv); //转而调用really_probe()
return ret;
}
h、really_probe(dev, drv) //调用driver的probe( ),dev为设备结构体
static atomic_t probe_count = ATOMIC_INIT(0);
static DECLARE_WAIT_QUEUE_HEAD(probe_waitqueue);
static int really_probe(struct device *dev, struct device_driver *drv)
{
int ret = 0;
atomic_inc(&probe_count);
pr_debug("bus: '%s': %s: probing driver %s with device %s
",
drv->bus->name, __func__, drv->name, dev_name(dev));
WARN_ON(!list_empty(&dev->devres_head));
dev->driver = drv;
if (driver_sysfs_add(dev)) {
printk(KERN_ERR "%s: driver_sysfs_add(%s) failed
",
__func__, dev_name(dev));
goto probe_failed;
}
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
} else if (drv->probe) {
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
driver_bound(dev);
ret = 1;
pr_debug("bus: '%s': %s: bound device %s to driver %s
",
drv->bus->name, __func__, dev_name(dev), drv->name);
goto done;
probe_failed:
devres_release_all(dev);
driver_sysfs_remove(dev);
dev->driver = NULL;
if (ret != -ENODEV && ret != -ENXIO) {
printk(KERN_WARNING
"%s: probe of %s failed with error %d
",
drv->name, dev_name(dev), ret);
}
ret = 0;
done:
atomic_dec(&probe_count);
wake_up(&probe_waitqueue);
return ret;
}
至此才开始执行程序中的probe函数。