fb的notifier

背景

最近调试了一个BUG:
一个平板使用hall传感器检测键盘保护套的开合,但是发现有时候合上保护套时,屏幕并没有关闭。

最后发现是CONFIG_FB=y配置没打开,导致hall传感器驱动里的fb的notifier没起作用,从而使获取的 suspend/resume 的状态异常,具体的细节不在这详述了,这里主要想扩展并记录下这个fbnotifier

使用

有时候,我们需要监听显示屏的一些事件,根据事件的状态来进行一些操作,比如常见的关闭显示屏时,触摸屏进入休眠等等

  1. 确认内核配置
    确认已打开CONFIG_FB_NOTIFY配置, 只要CONFIG_FB打开默认就打开了CONFIG_FB_NOTIFY
CONFIG_FB_CMDLINE=y
CONFIG_FB_NOTIFY=y
CONFIG_FB=y
  1. 代码实现
    这里主要以常用的FB_EVENT_BLANK事件为例,下面是部分伪代码实现:
struct yyy {
    ...
    struct notifier_block fb_notif; /*FB callback*/
};

static int fb_notifier_callback(struct notifier_block *self,
                unsigned long event, void *data)
{
    struct yyy *xxx;
    struct fb_event *event = data;

    xxx = container_of(self, struct yyy, fb_notif);

    if (action != FB_EVENT_BLANK)
        return NOTIFY_DONE;

    switch (*((int *)event->data)) {
    case FB_BLANK_UNBLANK:
        /* resume操作 */
        break;
    case FB_BLANK_POWERDOWN:
        /* suspend操作 */
        break;
    default:
        break;
    }

    return NOTIFY_OK;
}

static int zzz_probe(...)
{
    struct yyy *xxx;
    
    ...
    xxx->fb_notif.notifier_call = fb_notifier_callback;
    err = fb_register_client(&xxx->fb_notif);
    ...
}

static int zzz_remove(...)
{
    ...
    fb_unregister_client(&xxx->fb_notif);
    ...
}

fb blank的一些状态定义:

enum {
    /* screen: unblanked, hsync: on,  vsync: on */
    FB_BLANK_UNBLANK       = VESA_NO_BLANKING,

    /* screen: blanked,   hsync: on,  vsync: on */
    FB_BLANK_NORMAL        = VESA_NO_BLANKING + 1,

    /* screen: blanked,   hsync: on,  vsync: off */
    FB_BLANK_VSYNC_SUSPEND = VESA_VSYNC_SUSPEND + 1,

    /* screen: blanked,   hsync: off, vsync: on */
    FB_BLANK_HSYNC_SUSPEND = VESA_HSYNC_SUSPEND + 1,

    /* screen: blanked,   hsync: off, vsync: off */
    FB_BLANK_POWERDOWN     = VESA_POWERDOWN + 1
};

内部实现流程

fbnotifier其实就是一个 blocking notifier 可阻塞通知链:

static BLOCKING_NOTIFIER_HEAD(fb_notifier_list);

fb_register_client()函数里面就是调用的通知链注册函数,注册回调到fb的通知列表里面,fb_unregister_client()对应的就是相应的注销操作,具体实现在:drivers/video/fbdev/core/fb_notify.c,都是通知链常规操作的一层简单封装

然后在需要通知的地方调用fb_notifier_call_chain()函数,fb的通知主要集中在
drivers/video/fbdev/core/fbmem.c里面,我们常用的 blank 事件的通知在fb_blank()函数中:


int
fb_blank(struct fb_info *info, int blank)
{
    struct fb_event event;
    int ret = -EINVAL;

    if (blank > FB_BLANK_POWERDOWN)
        blank = FB_BLANK_POWERDOWN;

    event.info = info;
    event.data = ␣

    if (info->fbops->fb_blank)
        ret = info->fbops->fb_blank(blank, info);

    if (!ret)
        fb_notifier_call_chain(FB_EVENT_BLANK, &event);

    return ret;
}
EXPORT_SYMBOL(fb_blank);

参考