文章目录
基本理解Linux 内核事件通知链
内核事件通知链一个比较典型的例子就是Display 背光通知TP suspend和resume 的过程,我们这里先从的LCD 背光通知TP suspend 入场,以快速理解Linux 内核事件通知链应用。
1. TP 驱动相关代码
kernel-4.9/drivers/input/touchscreen/xxxxx_touch/xxxx_core.c
TP 驱动probe 函数里指定到背光通知回调函数fb_notifier_callback
,并注册到通知链里。fb_notifier_callback 函数根据收到的通知event信息调用TS resume 或suspend 函数。
static int xxx_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
... ...
ts_data->fb_notif.notifier_call = fb_notifier_callback;
ret = fb_register_client(&ts_data->fb_notif);
}
static int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data)
{
struct fb_event *evdata = data;
int *blank;
struct xxx_ts_data *xxx_data = container_of(self, struct xxx_ts_data, fb_notif);
printk("[xxx][WU] event = %ld, FB_EVENT_BLANK = %d or FB_EARLY_EVENT_BLANK = %d",
event, FB_EVENT_BLANK); //16 == 9 or 16; 9 == 9 or 16
if (evdata && evdata->data && event == FB_EARLY_EVENT_BLANK \
&& fts_data && fts_data->client) {
blank = evdata->data;
printk("[FTS][WU] blank = %d", *blank);
if (*blank == FB_BLANK_UNBLANK){
//0
printk("[FTS][WU] fts_ts_resume FB_BLANK_UNBLANK = %d", FB_BLANK_UNBLANK);
fts_ts_resume(&fts_data->client->dev);
}
else if (*blank == FB_BLANK_POWERDOWN){
//4
printk("[FTS][WU] fts_ts_suspend FB_BLANK_POWERDOWN = %d", FB_BLANK_POWERDOWN);
fts_ts_suspend(&fts_data->client->dev);
}
}
return 0;
}
注:若对上面container_of
有题问,可参考文章 《从基本理解到深入探究Linux kernel container_of 宏》
2. LCM 背光相关代码
kernel-4.9/drivers/video/fbdev/core/fbmem.c
LCM 背光代码根据相关场景给通知链发送相关事件的通知,通知链收到通知就执行上面的背光通知回调函数fb_notifier_callback,fb_notifier_callback函数根据不同event事件执行不同的函数。
int fb_blank(struct fb_info *info, int blank)
{
...
early_ret = fb_notifier_call_chain(FB_EARLY_EVENT_BLANK, &event);// FB_EARLY_EVENT_BLANK = 16
if (info->fbops->fb_blank)
ret = info->fbops->fb_blank(blank, info);
if (!ret)
fb_notifier_call_chain(FB_EVENT_BLANK, &event); //FB_EVENT_BLANK = 9
else {
/** if fb_blank is failed then revert effects of
* the early blank event. */
if (!early_ret)
fb_notifier_call_chain(FB_R_EARLY_EVENT_BLANK, &event);
}
return ret;
}
...
void fb_set_suspend(struct fb_info *info, int state)
{
struct fb_event event;
event.info = info;
if (state) {
fb_notifier_call_chain(FB_EVENT_SUSPEND, &event); // FB_EVENT_SUSPEND = 0x02
info->state = FBINFO_STATE_SUSPENDED;
} else {
info->state = FBINFO_STATE_RUNNING;
fb_notifier_call_chain(FB_EVENT_RESUME, &event); // FB_EVENT_RESUME = 0x03
}
}
3. 运行结果
下面是板子运行的log(已经屏蔽客户信息),我们在下面在log解释原理比较简明吧!
从运行结果可以知道背光通知TP过程中,先休眠TP再休眠LCM。
[32m[ 1045.412245] [33mPM[0m: suspend entry 2019-01-13 19:15:43.130364966 UTC // linux kernel 进入休眠
// 退出休眠与进入休眠的UTC时间戳一差,就是kernel 休眠的时间(19:15:55 - 19:15:43)
// 按电源键唤醒系统亮屏 ... ...
[32m[ 1050.799717] [33mPM[0m: suspend exit 2019-01-13 19:15:55.594697461 UTC // linux kernel 退出休眠
[32m[ 1051.144198] [0m[xxx][WU] event = 16, FB_EVENT_BLANK = 9 // 背光发送两次event,一次是16一次是9
[32m[ 1051.144211] [0m[xxx][WU] event = 9, FB_EVENT_BLANK = 9
[32m[ 1051.144221] [0m[xxx][WU] blank = 0 // 其中参数的blank 为0 调用TP resume
[32m[ 1051.144232] [0m[xxx][WU] xxx_ts_resume FB_BLANK_UNBLANK = 0
[32m[ 1051.144233] [33m[xxx]xxx_ts_resume[0m: Enter
[32m[ 1051.146266] [33m[xxx]xxx_ts_resume[0m: Exit(1668)
// 亮屏了10s后,按电源键灭屏 ... ...
[32m[ 1061.519665] [0m[xxx][WU] event = 16, FB_EVENT_BLANK = 9 // 背光发送两次event,一次是16一次是9
[32m[ 1061.519680] [0m[xxx][WU] event = 9, FB_EVENT_BLANK = 9
[32m[ 1061.519690] [0m[xxx][WU] blank = 4 // 其中参数的blank 为4 调用TP suspend
[32m[ 1061.519702] [0m[xxx][WU] xxx_ts_suspend FB_BLANK_POWERDOWN = 4
[32m[ 1061.519703] [33m[xxx]xxx_ts_suspend[0m: Enter
[32m[ 1061.523594] [33m[xxx]xxx_ts_suspend[0m: Exit(1609)
[32m[ 1061.528000] [33m mdss_dsi_panel_power_off // 然后display 下电
[32m[ 1061.529240] [33mhbtp[31m: hbtp->input_dev not ready!
[32m[ 1061.532073] [33mPM[0m: suspend entry 2019-01-13 19:16:06.327045373 UTC
...
[32m[ 1061.649485] [33mPM[0m: suspend exit 2019-01-13 19:16:06.444469957 UTC
[32m[ 1061.853378] [33mPM[0m: suspend entry 2019-01-13 19:16:06.648349227 UTC
[32m[ 1061.853419] [33mPM[0m: Syncing filesystems ... done.
[32m[ 1061.888011] [33mPM[0m: Preparing system for sleep (mem)
[32m[ 1061.888187] [0mFreezing user space processes ... (elapsed 3.964 seconds) done.
[32m[ 1065.852448] [0mFreezing remaining freezable tasks ... (elapsed 0.009 seconds) done.
[32m[ 1065.861693] [33mPM[0m: Suspending system (mem)
[32m[ 1065.861723] [0mSuspending console(s) (use no_console_suspend to debug)
// 因为TP已经休眠了, 上面 fb_set_suspend 函数也发来fb set supend 事件,不过这次已经无效了
[32m[ 1065.895347] [0m[xxx][WU] event = 2, FB_EVENT_BLANK = 9, FB_EVENT_SUSPEND = 2 // fb_set_suspend
[32m[ 1065.933676] [0m[xxx][WU] event = 2, FB_EVENT_BLANK = 9, FB_EVENT_SUSPEND = 2 // fb_set_suspend
[32m[ 1066.742596] [33mPM[0m: suspend exit 2019-01-13 19:16:27.002118646 UTC
总结下来就三个函数:
int fb_register_client(struct notifier_block *nb)
int fb_unregister_client(struct notifier_block *nb)
int fb_notifier_call_chain(unsigned long val, void *v)
上面三个函数就只是简单调用系统接口,如下代码 linux/drivers/video/fbdev/core/fb_notify.c
/*
* linux/drivers/video/fb_notify.c
*
* Copyright (C) 2006 Antonino Daplas <adaplas@pol.net>
*
* 2001 - Documented with DocBook
* - Brad Douglas <brad@neruo.com>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive
* for more details.
*/
#include <linux/fb.h>
#include <linux/notifier.h>
#include <linux/export.h>
static BLOCKING_NOTIFIER_HEAD(fb_notifier_list);
/**
* fb_register_client - register a client notifier
* @nb: notifier block to callback on events