Framebuffer 驱动学习总结(二)---- Framebuffer模块初始化
---恢复内容开始---
Framebuffer模块初始化过程:--driver\video\fbmem.c
1、 初始化Framebuffer:
FrameBuffer驱动是以模块的形式注册到系统中,在模块初始化时,创建FrameBuffer对应的设备文件及proc文件,并注册FrameBuffer设备操作接口函数fb_fops。
static int __init fbmem_init(void)
{
proc_create("fb", , NULL, &fb_proc_fops);///向 proc 文件系统报告驱动状态和参数
if (register_chrdev(FB_MAJOR,"fb",&fb_fops))///注册字符设备驱动,主设备号是29
printk("unable to get major %d for fb devs\n", FB_MAJOR);
fb_class = class_create(THIS_MODULE, "graphics");///创建 /sys/class/graphics 设备类,配合 mdev生成设备文件
if (IS_ERR(fb_class)) {
printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));
fb_class = NULL;
}
return ;
}
Framebuffer作为一个子系统,在fbmem_init中通过register_chrdev接口向系统注册一个主设备号位29的字符设备驱动。通过class_create创建graphics设备类,配合mdev机制生成供用户访问的设备文件(位于/dev目录)。
2、 Framebuffer设备驱动的接口集fb_fops的定义为:
static const struct file_operations fb_fops = {
.owner = THIS_MODULE,
.read = fb_read,
.write = fb_write,//二次拷贝
.unlocked_ioctl = fb_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = fb_compat_ioctl,
#endif
.mmap = fb_mmap,///映射,一次拷贝
.open = fb_open,
.release = fb_release,
#ifdef HAVE_ARCH_FB_UNMAPPED_AREA
.get_unmapped_area = get_fb_unmapped_area,
#endif
#ifdef CONFIG_FB_DEFERRED_IO
.fsync = fb_deferred_io_fsync,
#endif
.llseek = default_llseek,
};
在linux设备驱动中,所有的显示缓存设备均由framebuffer子系统内部管理,即linux设备驱动框架只认识一个主设备号为29的framebuffer设备。应用层所有针对显示缓存(最多32个)的访问均会推送给fb_fops进行进一步分发操作。
3、 注册Framebuffer:
linux 提供了register_framebuffer()和unregister_framebuffer()函数分别作为注册和注销帧缓冲设备,这两个函数都接受fb_info指针为参数,原型为:
int register_framebuffer(struct fb_info *fb_info);
int Unregister_framebuffer(struct fb_info *fb_info);
对于register_framebuffer()而言,如果注册的帧缓冲设备超过了FB_MAX(目前定义为32),则返回为-ENXIO,注册成功则返回为0。
int register_framebuffer(struct fb_info *fb_info)
{
int ret; mutex_lock(®istration_lock);
ret = do_register_framebuffer(fb_info);
mutex_unlock(®istration_lock); return ret;
}
EXPORT_SYMBOL(register_framebuffer);// 在内核的启动过程会被调用,以便执行注册帧缓冲区硬件设备的操作
static int do_register_framebuffer(struct fb_info *fb_info)
{
int i, ret;
struct fb_event event;
struct fb_videomode mode; if (fb_check_foreignness(fb_info))
return -ENOSYS; ret = do_remove_conflicting_framebuffers(fb_info->apertures,
fb_info->fix.id,
fb_is_primary_device(fb_info));
if (ret)
return ret; if (num_registered_fb == FB_MAX)//如果注册的帧缓冲设备超过了FB_MAX(目前定义为32),则返回为-ENXIO,注册成功则返回为0。
return -ENXIO; num_registered_fb++;///已经注册了的帧缓冲区硬件设备个数
for (i = ; i < FB_MAX; i++)
if (!registered_fb[i])///registered_fb[i]保存所有已经注册了的帧缓冲区硬件设备
break;
fb_info->node = i;
atomic_set(&fb_info->count, );
mutex_init(&fb_info->lock);
mutex_init(&fb_info->mm_lock); fb_info->dev = device_create(fb_class, fb_info->device,///在/sys/grapics/下创建 fbx设备,用于设备文件的创建
MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
if (IS_ERR(fb_info->dev)) {
/* Not fatal */
printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
fb_info->dev = NULL;
} else
fb_init_device(fb_info); if (fb_info->pixmap.addr == NULL) {
fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);
if (fb_info->pixmap.addr) {
fb_info->pixmap.size = FBPIXMAPSIZE;
fb_info->pixmap.buf_align = ;
fb_info->pixmap.scan_align = ;
fb_info->pixmap.access_align = ;
fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
}
}
fb_info->pixmap.offset = ; if (!fb_info->pixmap.blit_x)
fb_info->pixmap.blit_x = ~(u32); if (!fb_info->pixmap.blit_y)
fb_info->pixmap.blit_y = ~(u32); if (!fb_info->modelist.prev || !fb_info->modelist.next)
INIT_LIST_HEAD(&fb_info->modelist); if (fb_info->skip_vt_switch)
pm_vt_switch_required(fb_info->dev, false);
else
pm_vt_switch_required(fb_info->dev, true); fb_var_to_videomode(&mode, &fb_info->var);
fb_add_videomode(&mode, &fb_info->modelist);
registered_fb[i] = fb_info; event.info = fb_info;
console_lock();
if (!lock_fb_info(fb_info)) {
console_unlock();
return -ENODEV;
} fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);///通知帧缓冲区控制台,有一个新的帧缓冲区设备被注册到内核中来了
unlock_fb_info(fb_info);
console_unlock();
return ;
}
每个从设备都需要传递一个fb_info的数据结构指针,其即代表单个显示缓存设备。从中,可以看到fb_info最终会存储到全局数组struct fb_info*registered_fb[FB_MAX]中,FB_MAX是32,从这里我们也可以看出,framebuffer最多支持32个从设备。另外,每个从设备注册还会在/sys/class/graphics/设备类中创建一个设备,最终由mdev在/dev/目录中生成对应的设备文件。假设M个从设备调用register_framebuffer接口,即会在/dev中生成M个设备文件,如/dev/fb0、/dev/fb1、/dev/fb2等等。这M个设备的主设备号都是29,从设备则是0、1、2等等。
每一个被注册的帧缓冲区硬件设备在/dev/graphics目录下都有一个对应的设备文件fb<minor>,其中,<minor>表示一个从设备号。例如,第一个被注册的帧缓冲区硬件设备在/dev/graphics目录下都有一个对应的设备文件fb0。用户空间的应用程序通过这个设备文件就可以操作帧缓冲区硬件设备了,即将要显示的画面渲染到帧缓冲区硬件设备上去。
帧缓冲区控制台在内核中对应的驱动程序模块为fbcon: (drivers\video\console\Fbcon.c)
初始化:
static struct notifier_block fbcon_event_notifier = {
.notifier_call = fbcon_event_notify,
};
......
static int __init fb_console_init(void)//帧缓冲区控制台初始化
{
int i; console_lock();
fb_register_client(&fbcon_event_notifier);//调用fb_register_client来监听帧缓冲区硬件设备的注册事件,fbcon_event_notifier--->实现
fbcon_device = device_create(fb_class, NULL, MKDEV(, ), NULL,
"fbcon"); if (IS_ERR(fbcon_device)) {
printk(KERN_WARNING "Unable to create device "
"for fbcon; errno = %ld\n",
PTR_ERR(fbcon_device));
fbcon_device = NULL;
} else
fbcon_init_device(); for (i = ; i < MAX_NR_CONSOLES; i++)
con2fb_map[i] = -; console_unlock();
fbcon_start();
return ;
}
这个函数除了会调用函数device_create来创建一个类别为graphics的设备fbcon之外,还会调用函数fb_register_client来监听帧缓冲区硬件设备的注册事件,这是由函数fbcon_event_notify来实现的,如下所示:
static int fbcon_event_notify(struct notifier_block *self,
unsigned long action, void *data)
{
struct fb_event *event = data;
struct fb_info *info = event->info;
struct fb_videomode *mode;
struct fb_con2fbmap *con2fb;
struct fb_blit_caps *caps;
int idx, ret = ; switch(action) { ...... case FB_EVENT_FB_REGISTERED:
ret = fbcon_fb_registered(info);///---> 帧缓冲区硬件设备的注册事件最终是由函数fbcon_fb_registered来处理的
break; .......
}
done:
return ret;
}
帧缓冲区硬件设备的注册事件最终是由函数fbcon_fb_registered来处理的,它的实现如下所示:
static int fbcon_fb_registered(struct fb_info *info)
{
int ret = , i, idx; idx = info->node;
fbcon_select_primary(info);///检查当前注册的帧缓冲区硬件设备是否是一个主帧缓冲区硬件设备,如果是的话,那么就将它的信息记录下来 if (info_idx == -) {///如果是的话,那么就将它的信息记录下来
for (i = first_fb_vc; i <= last_fb_vc; i++) {
if (con2fb_map_boot[i] == idx) {
info_idx = idx;
break;
}
} if (info_idx != -)
ret = do_fbcon_takeover();
} else {
for (i = first_fb_vc; i <= last_fb_vc; i++) {
if (con2fb_map_boot[i] == idx)
set_con2fb_map(i, idx, );
}
} return ret;
}
函数fbcon_select_primary用来检查当前注册的帧缓冲区硬件设备是否是一个主帧缓冲区硬件设备。如果是的话,那么就将它的信息记录下来。这个函数只有当指定了CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY编译选项时才有效,否则的话,它是一个空函数。
在Linux内核中,每一个控制台和每一个帧缓冲区硬件设备都有一个从0开始的编号,它们的初始对应关系保存在全局数组con2fb_map_boot中。控制台和帧缓冲区硬件设备的初始对应关系是可以通过设置内核启动参数来初始化的。在模块fbcon中,还有另外一个全局数组con2fb_map,也是用来映射控制台和帧缓冲区硬件设备的对应关系,不过它映射的是控制台和帧缓冲区硬件设备的实际对应关系。
---恢复内容结束---
Framebuffer 驱动学习总结(二)---- Framebuffer模块初始化的更多相关文章
- nginx-push-stream模块源码学习(二)——模块初始化
本文重点介绍push stream模块的构成,至于nginx如何启动.维护该模块不会详细阐述,以后有时间会做详细阐述. 一.模块定义 1.1. 模块配置 通用nginx模块的配置struct有三种, ...
- Linux Framebuffer驱动剖析之二—驱动框架、接口实现和使用
深入分析LinuxFramebuffer子系统的驱动框架.接口实现和使用. 一.LinuxFramebuffer的软件需求 上一篇文章详细阐述了LinuxFramebuffer的软件需求(请先理解第一 ...
- Linux Framebuffer驱动框架之二软件架构(未完待续)【转】
本文转载自:http://blog.csdn.net/gqb_driver/article/details/12918547 /************************************ ...
- linux驱动学习(二) Makefile高级【转】
转自:http://blog.csdn.net/ghostyu/article/details/6866863 版权声明:本文为博主原创文章,未经博主允许不得转载. 在我前一篇写的[ linux驱动学 ...
- Framebuffer 驱动学习总结(一) ---- 总体架构及关键结构体
一.Framebuffer 设备驱动总体架构 帧缓冲设备为标准的字符型设备,在Linux中主设备号29,定义在/include/linux/major.h中的FB_MAJOR,次设备号定义帧缓冲的个数 ...
- Linux驱动学习之常用的模块操作命令
1.常用的模块操作命令 (1)lsmod(list module,将模块列表显示),功能是打印出当前内核中已经安装的模块列表 (2)insmod(install module,安装模块),功能是向当前 ...
- Webpack4 学习笔记二 CSS模块转换
前言 此内容是个人学习笔记,以便日后翻阅.非教程,如有错误还请指出 webpack 打包css模块 webpack是js模块打包器, 如果在入口文件引入css文件或其它的less.sass等文件,需要 ...
- SDRAM学习(二)之初始化
目录 1.SDRAM初始化的内容(结合英文数据手册) 2.SDRAM初始化的时序 3.代码的编写 4.modesim的仿真 SDRAM初始化的内容 SDRAMs must be powered up ...
- Linux内核驱动学习(二)添加自定义菜单到内核源码menuconfig
文章目录 目标 drivers/Kconfig demo下的Kconfig 和 Makefile Kconfig Makefile demo_gpio.c 目标 Kernel:Linux 4.4 我编 ...
随机推荐
- 远程连接工具SSH和linux的连接
实际开发中,Linux服务器都在其他的地方,我们要通过远程的方式去连接Linux并操作它,Linux远程的操作工具有很多,企业中常用的有Puttty.secureCRT.SSH Secure等.我使用 ...
- Hashtable 和 HashMap 以及 ConcurrentHashMap
备忘: ConcurrentHashMap与Hashtable的区别: Hashtable中采用的锁机制是一次锁住整个hash表,从而同一时刻只能由一个线程对其进行操作:而ConcurrentHash ...
- API接口测试中需要注意的地方
1.检查接口返回的数据是否与预期结果一致. 2.检查接口的容错性,假如传递数据的类型错误时是否可以处理.例如是支持整数,传递的是小数或字符串呢? 3.接口参数的边界值.例如,传递的参数足够大或为负数时 ...
- 读取Maven项目下resources目录下的配置文件(properties为例)
配置文件:xxxxx.properties a.url=******************** b.url=---------------------------------- 读取配置文件: im ...
- AtCoder Grand Contest 004
AtCoder Grand Contest 004 A - Divide a Cuboid 翻译 给定一个\(A*B*C\)的立方体,现在要把它分成两个立方体,求出他们的最小体积差. 题解 如果有一条 ...
- BZOJ 4753 [Jsoi2016]最佳团体 | 树上背包 分数规划
BZOJ 4753 [Jsoi2016]最佳团体 | 树上背包 分数规划 又是一道卡精度卡得我头皮发麻的题-- 题面(--蜜汁改编版) YL大哥是24OI的大哥,有一天,他想要从\(N\)个候选人中选 ...
- 一步步创建第一个Docker App —— 3. 创建一个集群Swarm
原文:https://docs.docker.com/engine/getstarted-voting-app/create-swarm/ 初始化集群 Swarm 1. 使用 ssh 命令登录 man ...
- yum报错Segmentation fault
yum install 安装一个包,提示 Segmentation fault,可以确定的是这个源肯定是可用的. 经查询,是 libz 这个库存在多个版本,导致冲突. # ldconfig -v | ...
- FastDFS分布式存储
分布式存储分类 通用分布式存储:mogilefs, fastdfs, ...(无文件系统接口, 通过API访问) 专用分布式存储:即分布式文件系统, moosefs, ...(有文件系统接口) GFS ...
- java线程池赏析
1.线程池的顶级接口(Executor) 线程池的顶级接口(jdk > 1.5).仅仅定义了方法execute(Runnable). 该方法接收一个Runnable实例,用来执行一个任务,该任务 ...