【Linux API】Sysfs & sysfs_notify文件系统

Sysfs是表现设备驱动模型的文件系统,涉及的结构体。

  • sysfs_dirent是组成sysfs单元的基本数据结构,它是sysfs文件夹或文件在内存中的代表。表示文件类型。增删文件实际是处理sysfs_dirent树
  • inode(index node)中保存了设备的主从设备号、一组文件操作函数和一组inode操作函数。
  • dentry(directory entry)的中文名称是目录项,是Linux文件系统中某个索引节点(inode)的链接。这个索引节点可以是文件,也可以是目录。

三个结构体的关系可以简单理解为:sysfs_dirent保存结构,inode保存文件信息,dentry保存连接。

sysfs_notify:可以理解为一种通信机制,用来唤醒在读写属性文件(sysfs节点)时因调用select()或poll()而阻塞的用户进程。

sysfs_notify 以及相关 API

sysfs_notify
函数作用:实质是调用sysfs_notify_dirent(),用来唤醒在读写属性文件(sysfs节点)时因调用select()或poll()而阻塞的用户进程。

1,sysfs_notify(struct kobject *k, const char *dir, const char *attr)
@ k :内核调用sysfs_create_group/sysfs_create_file创建sysfs节点时的struct kobject对象
@ dir:路径,所遇场景均使用NULL,不祥
@ attr:属性文件节点的名字,字符串

2, poll(struct pollfd fds[], nfds_t nfds, int timeout);
函数作用:查询是否可对设备进行无阻塞的访问,返回值大于0这可以访问。

 poll(struct pollfd fds[], nfds_t nfds, int timeout);
@ fds:pollfd结构数组,用于存放需要检测其状态的Socket描述符。表示要监听的事件以及响应之间的映射关系。请查询此结构体。
@ nfds:监听的文件的个数
@ timeout:是一个用毫秒表示的时间,是指定poll在返回前没有接收事件时应该等待的时间。如果  它的值为-1,poll就永远都不会超时。如果整数值为32个比特,那么最大的超时周期大约是30分钟。

3,int select(int, fd_set*, fd_set*, fd_set*, struct timeval*);
函数作用:系统调用查询是否可对设备进行无阻塞的访问,发生在内核,而poll发生在用户上层。

int select(int, fd_set*, fd_set*, fd_set*, struct timeval*);
@int :需要检查的文件描述符个数
@fdset :用来检查可读性的一组文件描述符
@fdset:用来检查可写性的一组文件描述符
@fdset:用来检查意外状态的文件描述符。(错误并不是意外状态)
@timeval:NULL指针代表无限等待,否则是指向timeval结构的指针,代表最长等待时间。

编程使用案例分析

Backlight驱动参考代码

参考代码:
/kernel/drivers/video/msm/mdss/mdss_fb.c
/hardware/qcom/display/libhwcomposer/hwc_vsync.cpp

创建sysfs属性节点
//1,创建节点名称以及节点操作函数

static DEVICE_ATTR(show_blank_event, S_IRUGO, mdss_mdp_show_blank_event, NULL);

static ssize_t mdss_mdp_show_blank_event(struct device *dev,
        struct device_attribute *attr, char *buf)  //用户使用cat命令查看
{
    struct fb_info *fbi = dev_get_drvdata(dev);
    struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par;
    int ret;

    pr_debug("fb%d panel_power_state = %d\n", mfd->index,
        mfd->panel_power_state);
    ret = scnprintf(buf, PAGE_SIZE, "panel_power_on = %d\n",
                        mfd->panel_power_state);

    return ret;
}
//2,将节点放入sysfs系统
static struct attribute *mdss_fb_attrs[] = {
         ……
    &dev_attr_show_blank_event.attr, //DEVICE_ATTR会将名字处理成dev_attr_xxx
        …….
};  //所有需要操作的节点,生成属性数组,统一创建

static struct attribute_group mdss_fb_attr_group = {
    .attrs = mdss_fb_attrs,
};//将数组转化生成属性集合,统一管理
static int mdss_fb_create_sysfs(struct msm_fb_data_type *mfd)
{
    int rc;
    rc = sysfs_create_group(&mfd->fbi->dev->kobj, &mdss_fb_attr_group); //统一创建sysfs节点

    if (rc)
        pr_err("sysfs group creation failed, rc=%d\n", rc);
    return rc;
}
static void mdss_fb_remove_sysfs(struct msm_fb_data_type *mfd)
{
    sysfs_remove_group(&mfd->fbi->dev->kobj, &mdss_fb_attr_group); //统一销毁sysfs节点
}
//3,节点信息发生变化,通知需要调用的用户进程
static int mdss_fb_blank_sub(int blank_mode, struct fb_info *info,
                 int op_enable){
     //一系列的状态检查与修改
    。。。。。
     //状态修改完成,通知用户进程
        /* Notify listeners */
    sysfs_notify(&mfd->fbi->dev->kobj, NULL, "show_blank_event");
}

//4,用户进程监听检查
struct event event_list[] =  {
    { "vsync_event", handle_vsync_event },
    { "show_blank_event", handle_blank_event },//封装成event.name ,event.callback函数,这个用户进程监听三个节点状态
    { "msm_fb_thermal_level", handle_thermal_event },
};
//event的回调事件按操作函数,这个节点只是简单的修改了sysfs下面节点的显示格式
static void handle_blank_event(hwc_context_t* ctx, int dpy, char *data)
{
    if (!strncmp(data, PANEL_ON_STR, strlen(PANEL_ON_STR))) {
        unsigned long int poweron = strtoul(data + strlen(PANEL_ON_STR), NULL, 0);
        ALOGI("%s: dpy:%d panel power state: %ld", __FUNCTION__, dpy, poweron);
        if (!ctx->mHDMIDisplay->isHDMIPrimaryDisplay()) {
            ctx->dpyAttr[dpy].isActive = poweron ? true: false;
        }
    }
}
//访问相应的节点,仅仅可读
pfd[dpy][ev].fd = open(node_path, O_RDONLY);

//用户线程hwcVsyncThread开启,poll查询状态以做出相应的响应,线程的操作函数中间如下代码

static void *vsync_loop(void *param) {
。。。。。
    if (LIKELY(!ctx->vstate.fakevsync)) {
        do {
            int err = poll(*pfd, (int)(num_displays * num_events), -1);  //监听检查当收到sysfs_notify(),返回值为大于0,则执行如下代码
            if(err > 0) { 
                for (int dpy = HWC_DISPLAY_PRIMARY; dpy < num_displays; dpy++) {
                    for(size_t ev = 0; ev < num_events; ev++) {
                        if (pfd[dpy][ev].revents & POLLPRI) {
                            // Clear vdata before writing into it
                            memset(&vdata, '\0', sizeof(vdata));
                            ssize_t len = pread(pfd[dpy][ev].fd, vdata,
                                               MAX_DATA - 1, 0);
                            if (UNLIKELY(len < 0)) {
                                // If the read was just interrupted - it is not
                                // a fatal error. Just continue in this case
                                ALOGE ("%s: Unable to read event:%zu for \
                                        dpy=%d : %s",
                                        __FUNCTION__, ev, dpy, strerror(errno));
                                continue;
                            }
                            vdata[len] = '\0';
                            event_list[ev].callback(ctx, dpy, vdata);
                        }
                    }
                }
            }
}
  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值