@The Linux driver implementer’s API guide->驱动模块之设备驱动设计央视
原文https://www.kernel.org/doc/html/latest/driver-api/driver-model/design-patterns.html
Device Driver Design Patterns设备驱动设计模式
本文档描述了设备驱动开发中一些通用的设计模式。子系统维护人员可能会要求驱动程序开发人员遵循这些设计模式。
- 状态容器
- container_of()
1.State Container状态容器
随谈我们往往假设内核中的这些设备驱动在某一个系统中智慧探测一次,但是实际情况下设备驱动绑定会发生好多次。这一位置probe()函数和所有的回调函数是可以重复调用的。
实现这个功能最通用的方式就是使用状态容器设计模式。其通常有这样的结构:
struct foo {
spinlock_t lock; /* Example member */
(...)
};
static int foo_probe(...)
{
struct foo *foo;
foo = devm_kzalloc(dev, sizeof(*foo), GFP_KERNEL);
if (!foo)
return -ENOMEM;
spin_lock_init(&foo->lock);
(...)
}
这样再次probe()函数创建时都会创建一个foo结构体的实例化对象。这就是我们这个设备驱动实例化的状态容器。当然有必要将这个状态实例传给所有需要访问状态和他的成员的函数。
例如,如果驱动桩侧了一个终端处理函数,你需要传递一个foo结构体的指针,就像这样:
static irqreturn_t foo_handler(int irq, void *arg)
{
struct foo *foo = arg;
(...)
}
static int foo_probe(...)
{
struct foo *foo;
(...)
ret = request_irq(irq, foo_handler, 0, "foo", foo);
}
这样你就可以在你的终端处理函数中获取正确的foo的实例化的指针。
2.container_of()
继续上述例程,我们添加一个卸载的工作。
struct foo {
spinlock_t lock;
struct workqueue_struct *wq;
struct work_struct offload;
(...)
};
static void foo_work(struct work_struct *work)
{
struct foo *foo = container_of(work, struct foo, offload);
(...)
}
static irqreturn_t foo_handler(int irq, void *arg)
{
struct foo *foo = arg;
queue_work(foo->wq, &foo->offload);
(...)
}
static int foo_probe(...)
{
struct foo *foo;
foo->wq = create_singlethread_workqueue("foo-wq");
INIT_WORK(&foo->offload, foo_work);
(...)
}
对于hrtimer或类似的东西,其设计模式是相同的,它将返回一个参数,该参数是指向回调中结构成员的指针。
container_of() 是一个宏定义,定义在<linux/kernel.h>
container_of()所做的是使用标准C中的offsetof()宏通过简单的减法从指向成员的指针获取指向包含结构的指针,这允许类似于面向对象行为的操作。请注意,所包含的成员不能是指针,而必须是实际成员,这样才能工作。
在这里我们可以看到,我们避免了以这种方式使用指向struct foo*实例的全局指针,同时仍然将传递给work函数的参数数量保持在单个指针上。