Device Driver Design Patterns ——State Container & container_of()

本文介绍了设备驱动开发中常见的两种设计模式:状态容器,用于保证代码的可重入性;container_of()技巧,帮助从成员指针获取结构实例。通过实例演示了如何使用这些模式来管理设备驱动的内部状态和回调函数。
摘要由CSDN通过智能技术生成

=============================

Device Driver Design Patterns

=============================

 

This document describes a few common design patterns found in device drivers.

It is likely that subsystem maintainers will ask driver developers to

conform to these design patterns.

 

1. State Container

2. container_of()


 

1. State Container

~~~~~~~~~~~~~~~~~~

 

While the kernel contains a few device drivers that assume that they will

only be probed() once on a certain system (singletons), it is custom to assume

that the device the driver binds to will appear in several instances. This

means that the probe() function and all callbacks need to be reentrant.

 

The most common way to achieve this is to use the state container design

pattern. It usually has this form::


  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);

      (...)

  }

 

This will create an instance of struct foo in memory every time probe() is

called. This is our state container for this instance of the device driver.

Of course it is then necessary to always pass this instance of the

state around to all functions that need access to the state and its members.

 

For example, if the driver is registering an interrupt handler, you would

pass around a pointer to struct foo like this::

 

  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);

  }

 

This way you always get a pointer back to the correct instance of foo in

your interrupt handler.


 

2. container_of()

~~~~~~~~~~~~~~~~~

 

Continuing on the above example we add an 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);

      (...)

  }

 

The design pattern is the same for an hrtimer or something similar that will

return a single argument which is a pointer to a struct member in the

callback.

 

container_of() is a macro defined in <linux/kernel.h>

 

What container_of() does is to obtain a pointer to the containing struct from

a pointer to a member by a simple subtraction using the offsetof() macro from

standard C, which allows something similar to object oriented behaviours.

Notice that the contained member must not be a pointer, but an actual member

for this to work.

 

We can see here that we avoid having global pointers to our struct foo *

instance this way, while still keeping the number of parameters passed to the

work function to a single pointer.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值