|
本文档描述了设备驱动程序中的一些常见设计模式。子系统维护者很可能会要求驱动程序开发人员遵守这些设计模式。
- State Container
- container_of()
1.State Container
虽然内核包含一些设备驱动程序,这些设备驱动程序假设它们只会在某个系统(单例)上被probed()一次,但通常假设驱动程序绑定到的设备将出现在多个实例中。这意味着 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() 时在内存中创建一个 struct foo 实例。这是这个设备驱动程序实例的状态容器。当然,有必要始终将状态的这个实例传递给需要访问状态及其成员的所有函数。
例如,如果驱动程序正在注册一个中断处理程序,您将传递一个指向 struct 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()
继续上面的例子,我们添加一个offloaded work:
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 * 实例,同时仍然将传递给工作函数的参数数量保持为单个指针。 |
|