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