转载:Linux notifier机制

linux庞大系统中,各个模块是相对独立的,那么模块间通信该如何做呢?当然你也可以使用全局资源,如果这样的话系统缺少独立性,会带来稳定性问题的。如果你说,使用共享内存,进程通信等,那么你曲解我的意思了,因为你说的大多是user space的,而我说的是内核模块级别的。notifier_chain,对就是它,实质上这个机制就是一个回调函数链表的操作,回调函数的注册,注销,调用。源系统处(比如A子系统)进行定义初始化回调函数调用,被通知处(比如B子系统)进行回调函数的注册注销,那么当A系统发生某种事件是,就调用通知链中所有回调函数,B系统中注册的回调函数就会得到执行。一旦执行回调函数,它会从链表头依次执行每一个回调函数,那么依次执行是一次性全部执行完?执行过程的任意时间都可睡眠?这些需求也就产生了4种类型的notifier_chain。


结构体定义


    
    
  1. struct notifier_block { /* chain的基本单位 */
  2. int (*notifier_call)(struct notifier_block *, unsigned long, void *);
  3. struct notifier_block __rcu *next;
  4. int priority;
  5. };
  6. struct atomic_notifier_head { /* atmoic context; 执行(rcu_read_lock);回调函数执行不能阻塞;实时性高 */
  7. spinlock_t lock;
  8. struct notifier_block __rcu *head;
  9. };
  10. struct blocking_notifier_head { /* process context;执行(rw_semaphore) ;回调函数执行可以阻塞;实时性相对低*/
  11. struct rw_semaphore rwsem;
  12. struct notifier_block __rcu *head;
  13. };
  14. struct raw_notifier_head { /* 原始链表操作,注册,执行过程无任何保护,完全有驱动人员控制 */
  15. struct notifier_block __rcu *head;
  16. };
  17. struct srcu_notifier_head { /* process context;可阻塞通知链的变体(Sleepable Read-Copy-Update),回调函数执行可以阻塞 */
  18. struct mutex mutex;
  19. struct srcu_struct srcu;
  20. struct notifier_block __rcu *head;
  21. };
notifier_chain的API使用基本四大步骤:定义初始化、注册、调用和注销,而这些操作的基本单位是notifier_block,这个基本单位中包含了回调函数notifier_call,后继notifier_block指针,优先级priority(默认0,数字越大优先级越高,越靠近链表的头结点,越优先得到执行)。

初始化

#include <linux/notifier.h>


    
    
  1. #define ATOMIC_NOTIFIER_HEAD(name) \
  2. struct atomic_notifier_head name = \
  3. ATOMIC_NOTIFIER_INIT(name)
  4. #define BLOCKING_NOTIFIER_HEAD(name) \
  5. struct blocking_notifier_head name = \
  6. BLOCKING_NOTIFIER_INIT(name)
  7. #define RAW_NOTIFIER_HEAD(name) \
  8. struct raw_notifier_head name = \
  9. RAW_NOTIFIER_INIT(name)

    
    
  1. /* srcu_notifier_heads must be initialized and cleaned up dynamically */
  2. extern void srcu_init_notifier_head(struct srcu_notifier_head *nh);
  3. #define srcu_cleanup_notifier_head(name) \
  4. cleanup_srcu_struct(&(name)->srcu);

 
   经过定义及初始化后,一类的链表的头就形成了,注册就是增加节点,执行就是遍历节点,唯一要说明的就是,srcu链需要动态的定义,其他三中不需要。 
   

注册和注销


    
    
  1. int atomic_notifier_chain_register(struct atomic_notifier_head *nh, struct notifier_block *n);
  2. int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh, struct notifier_block *n)

    
    
  1. int blocking_notifier_chain_register(struct blocking_notifier_head *nh, struct notifier_block *n);
  2. /* 注册的notifier_block不重复*/
  3. int blocking_notifier_chain_cond_register(struct blocking_notifier_head *nh, struct notifier_block *n);
  4. int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh, struct notifier_block *n);

    
    
  1. int raw_notifier_chain_register(struct raw_notifier_head *nh, struct notifier_block *n);
  2. int raw_notifier_chain_unregister(struct raw_notifier_head *nh, struct notifier_block *n);

    
    
  1. int srcu_notifier_chain_register(struct srcu_notifier_head *nh, struct notifier_block *n);
  2. int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh, struct notifier_block *n);

其中注册和注销都调用了最基本的函数如下:


    
    
  1. /*
  2. * Notifier chain core routines. The exported routines below
  3. * are layered on top of these, with appropriate locking added.
  4. */
  5. static int notifier_chain_register(struct notifier_block **nl,
  6. struct notifier_block *n)
  7. {
  8. while ((*nl) != NULL) {
  9. if (n->priority > (*nl)->priority)
  10. break;
  11. nl = &((*nl)->next);
  12. }
  13. n->next = *nl;
  14. rcu_assign_pointer(*nl, n);
  15. return 0;
  16. }
  17. static int notifier_chain_cond_register(struct notifier_block **nl,
  18. struct notifier_block *n)
  19. {
  20. while ((*nl) != NULL) {
  21. if ((*nl) == n)
  22. return 0;
  23. if (n->priority > (*nl)->priority)
  24. break;
  25. nl = &((*nl)->next);
  26. }
  27. n->next = *nl;
  28. rcu_assign_pointer(*nl, n);
  29. return 0;
  30. }
  31. static int notifier_chain_unregister(struct notifier_block **nl,
  32. struct notifier_block *n)
  33. {
  34. while ((*nl) != NULL) {
  35. if ((*nl) == n) {
  36. rcu_assign_pointer(*nl, n->next);
  37. return 0;
  38. }
  39. nl = &((*nl)->next);
  40. }
  41. return -ENOENT;
  42. }


调用

当A系统发生事件时,会调用下面函数之一的方法来,执行通知链中的所有回调函数,那么所有注册的地方都会得到执行,除前一个函数执行返回值含有NOTIFY_STOP_MASK标志。其中参数val是一个整形,用户自定义含义的传入值,参数v是一个void*类型,用户自定义任何类型含义,一般为平台结构体指针,使用的时候需要强制转换。


    
    
  1. /*调用了__atomic_notifier_call_chain,且nr_to_call=-1表示回调函数调用个数不限,nr_calls=NULL表示不关心已执行的回调函数个数*/
  2. extern int atomic_notifier_call_chain(struct atomic_notifier_head *nh, unsigned long val, void *v);
  3. extern int __atomic_notifier_call_chain(struct atomic_notifier_head *nh, unsigned long val, void *v, int nr_to_call, int *nr_calls);
  4. extern int blocking_notifier_call_chain(struct blocking_notifier_head *nh, unsigned long val, void *v);
  5. <span style= "white-space:pre"> </span> extern int __blocking_notifier_call_chain(struct blocking_notifier_head *nh, unsigned long val, void *v, int nr_to_call, int *nr_calls);
  6. extern int raw_notifier_call_chain(struct raw_notifier_head *nh, unsigned long val, void *v);
  7. extern int __raw_notifier_call_chain(struct raw_notifier_head *nh, unsigned long val, void *v, int nr_to_call, int *nr_calls);
  8. extern int srcu_notifier_call_chain(struct srcu_notifier_head *nh, unsigned long val, void *v);
  9. extern int __srcu_notifier_call_chain(struct srcu_notifier_head *nh, unsigned long val, void *v, int nr_to_call, int *nr_calls);

回调函数返回值


    
    
  1. #define NOTIFY_DONE 0x0000 /* Don't care回调函数不关心返回值*/
  2. #define NOTIFY_OK 0x0001 /* Suits me 回调函数调用顺利完成*/
  3. #define NOTIFY_STOP_MASK 0x8000 /* Don't call further 回调函数链禁止继续调用的掩码*/
  4. #define NOTIFY_BAD (NOTIFY_STOP_MASK|0x0002)
  5. /* Bad/Veto action 回调函数执行有错*/
  6. /*
  7. * Clean way to return from the notifier and stop further calls.当前顺利调用,禁止继续调用
  8. */
  9. #define NOTIFY_STOP (NOTIFY_OK|NOTIFY_STOP_MASK)

在notifier_call_chain函数中去依次执行每一个注册的回调函数,并以前一个回调函数的返回值为判断依据,是否继续调用,最后一个执行函数的返回值作为notifier_call_chain的返回值。当前标准API中,只关注了NOTIFY_STOP_MASK,其他的在notifier_call_chain中未做处理。


典型用例

在linux中,液晶显示器会提供一个fb_notify,当显示器发生某种事件时,会调用notifier_call_chain,这样注册的回调函数得到执行,回调函数根据显示的framebuffer状态执行你预想的动作。
它选用通知链类型的是blocking_notifier_chain。

     
     
  1. /*
  2. * linux/drivers/video/fb_notify.c
  3. *
  4. * Copyright (C) 2006 Antonino Daplas <[email protected]>
  5. *
  6. * 2001 - Documented with DocBook
  7. * - Brad Douglas <[email protected]>
  8. *
  9. * This file is subject to the terms and conditions of the GNU General Public
  10. * License. See the file COPYING in the main directory of this archive
  11. * for more details.
  12. */
  13. #include <linux/fb.h>
  14. #include <linux/notifier.h>
  15. #include <linux/export.h>
  16. /*静态定义并初始化通知链头*/
  17. static BLOCKING_NOTIFIER_HEAD(fb_notifier_list);
  18. /** 注册回调函数,加入一个回调函数节点
  19. * fb_register_client - register a client notifier
  20. * @nb: notifier block to callback on events
  21. */
  22. int fb_register_client(struct notifier_block *nb)
  23. {
  24. return blocking_notifier_chain_register(&fb_notifier_list, nb);
  25. }
  26. EXPORT_SYMBOL(fb_register_client);
  27. /** 注销回调函数,删除一个回调函数节点
  28. * fb_unregister_client - unregister a client notifier
  29. * @nb: notifier block to callback on events
  30. */
  31. int fb_unregister_client(struct notifier_block *nb)
  32. {
  33. return blocking_notifier_chain_unregister(&fb_notifier_list, nb);
  34. }
  35. EXPORT_SYMBOL(fb_unregister_client);
  36. /** 当源即framebuffer发生某种事件,调用该函数执行所有回调函数,通知其他子系统
  37. * fb_notifier_call_chain - notify clients of fb_events
  38. *
  39. */
  40. int fb_notifier_call_chain(unsigned long val, void *v)
  41. {
  42. return blocking_notifier_call_chain(&fb_notifier_list, val, v);
  43. }
  44. EXPORT_SYMBOL_GPL(fb_notifier_call_chain);

假如framebuffer为A子系统,触屏ft5x06为B子系统,现想要做到触屏伴随显示屏息屏而休眠,亮屏而唤醒。


B子系统(触屏)/被通知者,代码如下:

     
     
  1. kernel\drivers\input\touchscreen\ft5x06_ts.c
  2. #if defined(CONFIG_FB)
  3. static int fb_notifier_callback(struct notifier_block *self,
  4. unsigned long event, void *data)
  5. {
  6. struct fb_event *evdata = data;
  7. int *blank;
  8. struct ft5x06_ts_data *ft5x06_data =
  9. container_of( self, struct ft5x06_ts_data, fb_notif);
  10. /* 检测是否是显示器BLANK改变事件 */
  11. if (evdata && evdata->data && event == FB_EVENT_BLANK &&
  12. ft5x06_data && ft5x06_data->client) {
  13. blank = evdata->data;
  14. if (*blank == FB_BLANK_UNBLANK) /*是BLANK事件中的LCD亮屏事件,唤醒触屏*/
  15. ft5x06_ts_resume(&ft5x06_data->client->dev);
  16. else if (*blank == FB_BLANK_POWERDOWN) /*是BLANK事件中的LCD灭屏事件,让触屏休眠 */
  17. ft5x06_ts_suspend(&ft5x06_data->client->dev);
  18. }
  19. return 0;
  20. }
  21. #elif defined(CONFIG_HAS_EARLYSUSPEND)
  22. //……
  23. #endif
  24. static int ft5x06_ts_probe(struct i2c_client *client,
  25. const struct i2c_device_id *id)
  26. {
  27. //……
  28. #if defined(CONFIG_FB)
  29. data->fb_notif.notifier_call = fb_notifier_callback;
  30. /*注册fb回调函数*/
  31. err = fb_register_client(&data->fb_notif);
  32. if (err)
  33. dev_err(&client->dev, “Unable to register fb_notifier: %d\n”,
  34. err);
  35. #endif
  36. //……
  37. }
  38. static int __ devexit ft5x06_ts_remove(struct i2c_client *client)
  39. {
  40. //……
  41. #if defined(CONFIG_FB)
  42. /*注销fb回调函数*/
  43. if (fb_unregister_client(&data->fb_notif))
  44. dev_err(&client->dev, “Error occurred while unregistering fb_notifier.\n”);
  45. #elif defined(CONFIG_HAS_EARLYSUSPEND)
  46. unregister_early_suspend(&data->early_suspend);
  47. #endif
  48. //……
  49. }

A子系统(framebuffer)/通知者,代码如下:

     
     
  1. int fb_blank(struct fb_info *info, int blank)
  2. {
  3. int ret = -EINVAL;
  4. if (blank > FB_BLANK_POWERDOWN)
  5. blank = FB_BLANK_POWERDOWN;
  6. if (info->fbops->fb_blank) /*硬件执行亮屏还是灭屏的操作*/
  7. ret = info->fbops->fb_blank(blank, info);
  8. if (!ret) {
  9. struct fb_event event;
  10. event.info = info;
  11. event.data = ␣
  12. /*硬件BLANK操作成功后,调用所有的注册回调函数,比如通知给触屏*/
  13. fb_notifier_call_chain(FB_EVENT_BLANK, &event);
  14. }
  15. return ret;
  16. }
  17. /*fbmem中的ioctl*/
  18. static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
  19. unsigned long arg)
  20. {
  21. //…….
  22. case FBIOBLANK: //由上层发下来的亮屏还是息屏的IO命令
  23. if (!lock_fb_info(info))
  24. return -ENODEV;
  25. console_lock();
  26. info->flags |= FBINFO_MISC_USEREVENT;
  27. ret = fb_blank(info, arg);
  28. info->flags &= ~FBINFO_MISC_USEREVENT;
  29. console_unlock();
  30. unlock_fb_info(info);
  31. break;
  32. //……
  33. }

至于framebuffer中的notifier_call_chain的第二、三个参数,详见linux\fb.h,fbmem.c,fbcon.c。有时间也写个关于fb相关的文章。


        </div>
            </div>

linux庞大系统中,各个模块是相对独立的,那么模块间通信该如何做呢?当然你也可以使用全局资源,如果这样的话系统缺少独立性,会带来稳定性问题的。如果你说,使用共享内存,进程通信等,那么你曲解我的意思了,因为你说的大多是user space的,而我说的是内核模块级别的。notifier_chain,对就是它,实质上这个机制就是一个回调函数链表的操作,回调函数的注册,注销,调用。源系统处(比如A子系统)进行定义初始化回调函数调用,被通知处(比如B子系统)进行回调函数的注册注销,那么当A系统发生某种事件是,就调用通知链中所有回调函数,B系统中注册的回调函数就会得到执行。一旦执行回调函数,它会从链表头依次执行每一个回调函数,那么依次执行是一次性全部执行完?执行过程的任意时间都可睡眠?这些需求也就产生了4种类型的notifier_chain。


结构体定义


  
  
  1. struct notifier_block { /* chain的基本单位 */
  2. int (*notifier_call)(struct notifier_block *, unsigned long, void *);
  3. struct notifier_block __rcu *next;
  4. int priority;
  5. };
  6. struct atomic_notifier_head { /* atmoic context; 执行(rcu_read_lock);回调函数执行不能阻塞;实时性高 */
  7. spinlock_t lock;
  8. struct notifier_block __rcu *head;
  9. };
  10. struct blocking_notifier_head { /* process context;执行(rw_semaphore) ;回调函数执行可以阻塞;实时性相对低*/
  11. struct rw_semaphore rwsem;
  12. struct notifier_block __rcu *head;
  13. };
  14. struct raw_notifier_head { /* 原始链表操作,注册,执行过程无任何保护,完全有驱动人员控制 */
  15. struct notifier_block __rcu *head;
  16. };
  17. struct srcu_notifier_head { /* process context;可阻塞通知链的变体(Sleepable Read-Copy-Update),回调函数执行可以阻塞 */
  18. struct mutex mutex;
  19. struct srcu_struct srcu;
  20. struct notifier_block __rcu *head;
  21. };
notifier_chain的API使用基本四大步骤:定义初始化、注册、调用和注销,而这些操作的基本单位是notifier_block,这个基本单位中包含了回调函数notifier_call,后继notifier_block指针,优先级priority(默认0,数字越大优先级越高,越靠近链表的头结点,越优先得到执行)。

初始化

#include <linux/notifier.h>


  
  
  1. #define ATOMIC_NOTIFIER_HEAD(name) \
  2. struct atomic_notifier_head name = \
  3. ATOMIC_NOTIFIER_INIT(name)
  4. #define BLOCKING_NOTIFIER_HEAD(name) \
  5. struct blocking_notifier_head name = \
  6. BLOCKING_NOTIFIER_INIT(name)
  7. #define RAW_NOTIFIER_HEAD(name) \
  8. struct raw_notifier_head name = \
  9. RAW_NOTIFIER_INIT(name)

  
  
  1. /* srcu_notifier_heads must be initialized and cleaned up dynamically */
  2. extern void srcu_init_notifier_head(struct srcu_notifier_head *nh);
  3. #define srcu_cleanup_notifier_head(name) \
  4. cleanup_srcu_struct(&(name)->srcu);

 
 经过定义及初始化后,一类的链表的头就形成了,注册就是增加节点,执行就是遍历节点,唯一要说明的就是,srcu链需要动态的定义,其他三中不需要。 
 

注册和注销


  
  
  1. int atomic_notifier_chain_register(struct atomic_notifier_head *nh, struct notifier_block *n);
  2. int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh, struct notifier_block *n)

  
  
  1. int blocking_notifier_chain_register(struct blocking_notifier_head *nh, struct notifier_block *n);
  2. /* 注册的notifier_block不重复*/
  3. int blocking_notifier_chain_cond_register(struct blocking_notifier_head *nh, struct notifier_block *n);
  4. int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh, struct notifier_block *n);

  
  
  1. int raw_notifier_chain_register(struct raw_notifier_head *nh, struct notifier_block *n);
  2. int raw_notifier_chain_unregister(struct raw_notifier_head *nh, struct notifier_block *n);

  
  
  1. int srcu_notifier_chain_register(struct srcu_notifier_head *nh, struct notifier_block *n);
  2. int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh, struct notifier_block *n);

其中注册和注销都调用了最基本的函数如下:


  
  
  1. /*
  2. * Notifier chain core routines. The exported routines below
  3. * are layered on top of these, with appropriate locking added.
  4. */
  5. static int notifier_chain_register(struct notifier_block **nl,
  6. struct notifier_block *n)
  7. {
  8. while ((*nl) != NULL) {
  9. if (n->priority > (*nl)->priority)
  10. break;
  11. nl = &((*nl)->next);
  12. }
  13. n->next = *nl;
  14. rcu_assign_pointer(*nl, n);
  15. return 0;
  16. }
  17. static int notifier_chain_cond_register(struct notifier_block **nl,
  18. struct notifier_block *n)
  19. {
  20. while ((*nl) != NULL) {
  21. if ((*nl) == n)
  22. return 0;
  23. if (n->priority > (*nl)->priority)
  24. break;
  25. nl = &((*nl)->next);
  26. }
  27. n->next = *nl;
  28. rcu_assign_pointer(*nl, n);
  29. return 0;
  30. }
  31. static int notifier_chain_unregister(struct notifier_block **nl,
  32. struct notifier_block *n)
  33. {
  34. while ((*nl) != NULL) {
  35. if ((*nl) == n) {
  36. rcu_assign_pointer(*nl, n->next);
  37. return 0;
  38. }
  39. nl = &((*nl)->next);
  40. }
  41. return -ENOENT;
  42. }


调用

当A系统发生事件时,会调用下面函数之一的方法来,执行通知链中的所有回调函数,那么所有注册的地方都会得到执行,除前一个函数执行返回值含有NOTIFY_STOP_MASK标志。其中参数val是一个整形,用户自定义含义的传入值,参数v是一个void*类型,用户自定义任何类型含义,一般为平台结构体指针,使用的时候需要强制转换。


  
  
  1. /*调用了__atomic_notifier_call_chain,且nr_to_call=-1表示回调函数调用个数不限,nr_calls=NULL表示不关心已执行的回调函数个数*/
  2. extern int atomic_notifier_call_chain(struct atomic_notifier_head *nh, unsigned long val, void *v);
  3. extern int __atomic_notifier_call_chain(struct atomic_notifier_head *nh, unsigned long val, void *v, int nr_to_call, int *nr_calls);
  4. extern int blocking_notifier_call_chain(struct blocking_notifier_head *nh, unsigned long val, void *v);
  5. <span style= "white-space:pre"> </span> extern int __blocking_notifier_call_chain(struct blocking_notifier_head *nh, unsigned long val, void *v, int nr_to_call, int *nr_calls);
  6. extern int raw_notifier_call_chain(struct raw_notifier_head *nh, unsigned long val, void *v);
  7. extern int __raw_notifier_call_chain(struct raw_notifier_head *nh, unsigned long val, void *v, int nr_to_call, int *nr_calls);
  8. extern int srcu_notifier_call_chain(struct srcu_notifier_head *nh, unsigned long val, void *v);
  9. extern int __srcu_notifier_call_chain(struct srcu_notifier_head *nh, unsigned long val, void *v, int nr_to_call, int *nr_calls);

回调函数返回值


  
  
  1. #define NOTIFY_DONE 0x0000 /* Don't care回调函数不关心返回值*/
  2. #define NOTIFY_OK 0x0001 /* Suits me 回调函数调用顺利完成*/
  3. #define NOTIFY_STOP_MASK 0x8000 /* Don't call further 回调函数链禁止继续调用的掩码*/
  4. #define NOTIFY_BAD (NOTIFY_STOP_MASK|0x0002)
  5. /* Bad/Veto action 回调函数执行有错*/
  6. /*
  7. * Clean way to return from the notifier and stop further calls.当前顺利调用,禁止继续调用
  8. */
  9. #define NOTIFY_STOP (NOTIFY_OK|NOTIFY_STOP_MASK)

在notifier_call_chain函数中去依次执行每一个注册的回调函数,并以前一个回调函数的返回值为判断依据,是否继续调用,最后一个执行函数的返回值作为notifier_call_chain的返回值。当前标准API中,只关注了NOTIFY_STOP_MASK,其他的在notifier_call_chain中未做处理。


典型用例

在linux中,液晶显示器会提供一个fb_notify,当显示器发生某种事件时,会调用notifier_call_chain,这样注册的回调函数得到执行,回调函数根据显示的framebuffer状态执行你预想的动作。
它选用通知链类型的是blocking_notifier_chain。

   
   
  1. /*
  2. * linux/drivers/video/fb_notify.c
  3. *
  4. * Copyright (C) 2006 Antonino Daplas <[email protected]>
  5. *
  6. * 2001 - Documented with DocBook
  7. * - Brad Douglas <[email protected]>
  8. *
  9. * This file is subject to the terms and conditions of the GNU General Public
  10. * License. See the file COPYING in the main directory of this archive
  11. * for more details.
  12. */
  13. #include <linux/fb.h>
  14. #include <linux/notifier.h>
  15. #include <linux/export.h>
  16. /*静态定义并初始化通知链头*/
  17. static BLOCKING_NOTIFIER_HEAD(fb_notifier_list);
  18. /** 注册回调函数,加入一个回调函数节点
  19. * fb_register_client - register a client notifier
  20. * @nb: notifier block to callback on events
  21. */
  22. int fb_register_client(struct notifier_block *nb)
  23. {
  24. return blocking_notifier_chain_register(&fb_notifier_list, nb);
  25. }
  26. EXPORT_SYMBOL(fb_register_client);
  27. /** 注销回调函数,删除一个回调函数节点
  28. * fb_unregister_client - unregister a client notifier
  29. * @nb: notifier block to callback on events
  30. */
  31. int fb_unregister_client(struct notifier_block *nb)
  32. {
  33. return blocking_notifier_chain_unregister(&fb_notifier_list, nb);
  34. }
  35. EXPORT_SYMBOL(fb_unregister_client);
  36. /** 当源即framebuffer发生某种事件,调用该函数执行所有回调函数,通知其他子系统
  37. * fb_notifier_call_chain - notify clients of fb_events
  38. *
  39. */
  40. int fb_notifier_call_chain(unsigned long val, void *v)
  41. {
  42. return blocking_notifier_call_chain(&fb_notifier_list, val, v);
  43. }
  44. EXPORT_SYMBOL_GPL(fb_notifier_call_chain);

假如framebuffer为A子系统,触屏ft5x06为B子系统,现想要做到触屏伴随显示屏息屏而休眠,亮屏而唤醒。


B子系统(触屏)/被通知者,代码如下:

   
   
  1. kernel\drivers\input\touchscreen\ft5x06_ts.c
  2. #if defined(CONFIG_FB)
  3. static int fb_notifier_callback(struct notifier_block *self,
  4. unsigned long event, void *data)
  5. {
  6. struct fb_event *evdata = data;
  7. int *blank;
  8. struct ft5x06_ts_data *ft5x06_data =
  9. container_of( self, struct ft5x06_ts_data, fb_notif);
  10. /* 检测是否是显示器BLANK改变事件 */
  11. if (evdata && evdata->data && event == FB_EVENT_BLANK &&
  12. ft5x06_data && ft5x06_data->client) {
  13. blank = evdata->data;
  14. if (*blank == FB_BLANK_UNBLANK) /*是BLANK事件中的LCD亮屏事件,唤醒触屏*/
  15. ft5x06_ts_resume(&ft5x06_data->client->dev);
  16. else if (*blank == FB_BLANK_POWERDOWN) /*是BLANK事件中的LCD灭屏事件,让触屏休眠 */
  17. ft5x06_ts_suspend(&ft5x06_data->client->dev);
  18. }
  19. return 0;
  20. }
  21. #elif defined(CONFIG_HAS_EARLYSUSPEND)
  22. //……
  23. #endif
  24. static int ft5x06_ts_probe(struct i2c_client *client,
  25. const struct i2c_device_id *id)
  26. {
  27. //……
  28. #if defined(CONFIG_FB)
  29. data->fb_notif.notifier_call = fb_notifier_callback;
  30. /*注册fb回调函数*/
  31. err = fb_register_client(&data->fb_notif);
  32. if (err)
  33. dev_err(&client->dev, “Unable to register fb_notifier: %d\n”,
  34. err);
  35. #endif
  36. //……
  37. }
  38. static int __ devexit ft5x06_ts_remove(struct i2c_client *client)
  39. {
  40. //……
  41. #if defined(CONFIG_FB)
  42. /*注销fb回调函数*/
  43. if (fb_unregister_client(&data->fb_notif))
  44. dev_err(&client->dev, “Error occurred while unregistering fb_notifier.\n”);
  45. #elif defined(CONFIG_HAS_EARLYSUSPEND)
  46. unregister_early_suspend(&data->early_suspend);
  47. #endif
  48. //……
  49. }

A子系统(framebuffer)/通知者,代码如下:

   
   
  1. int fb_blank(struct fb_info *info, int blank)
  2. {
  3. int ret = -EINVAL;
  4. if (blank > FB_BLANK_POWERDOWN)
  5. blank = FB_BLANK_POWERDOWN;
  6. if (info->fbops->fb_blank) /*硬件执行亮屏还是灭屏的操作*/
  7. ret = info->fbops->fb_blank(blank, info);
  8. if (!ret) {
  9. struct fb_event event;
  10. event.info = info;
  11. event.data = ␣
  12. /*硬件BLANK操作成功后,调用所有的注册回调函数,比如通知给触屏*/
  13. fb_notifier_call_chain(FB_EVENT_BLANK, &event);
  14. }
  15. return ret;
  16. }
  17. /*fbmem中的ioctl*/
  18. static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
  19. unsigned long arg)
  20. {
  21. //…….
  22. case FBIOBLANK: //由上层发下来的亮屏还是息屏的IO命令
  23. if (!lock_fb_info(info))
  24. return -ENODEV;
  25. console_lock();
  26. info->flags |= FBINFO_MISC_USEREVENT;
  27. ret = fb_blank(info, arg);
  28. info->flags &= ~FBINFO_MISC_USEREVENT;
  29. console_unlock();
  30. unlock_fb_info(info);
  31. break;
  32. //……
  33. }

至于framebuffer中的notifier_call_chain的第二、三个参数,详见linux\fb.h,fbmem.c,fbcon.c。有时间也写个关于fb相关的文章。


        </div>
            </div>

猜你喜欢

转载自blog.csdn.net/qq_21547927/article/details/81806163