framebuffer子系统

概述

本系列文章将分析framebuffer的驱动作用(需求)、框架、接口实现和使用。按笔者一直倡导的Linuc学习理念---从软件需求的角度去理解Linux,对于Linux各个子系统,我们首要理解其软件需求,从中自然会清楚其存在的价值和作用;接下来是理解子系统在 Linux整个驱动框架中的层次、角色和如何交换;最后是理解驱动的接口如何实现软件需求,明确接口如何在各场景中使用。


Linux设备驱动和裸设备驱动的关系

理解Framebuffer需求之前,我们先理解Linux设备驱动和裸设备驱动的关系。

例如,对于LCD液晶屏,其可由三星研发的SOC S5PV210的多媒体硬件模块所支持。而对于具体的某种LCD液晶屏,涉及到分辨率、实验参数等不同,需要通过软件来设置相应的SOC多媒体硬件寄存器,以达到控制显示的目的。这个软件设置就是SOC编程,其不管是SOC上运行的是Linux,还是Windows,或者是IOS,软件设置的最终结果提现到寄存器上都是一样的。一般地,嵌入式都是C语言研发,而高级处理器的寄存器是统一编制的,因此裸设备驱动外围设备的C语言代码基本是一致的。

在代操作系统运行时,为了安全考虑,系统一般分为用户态和内核态。那么,SOC编程时硬件编程,只能在内核态完成,并且需要向用户态程序提供一个接口以进行调用,对于不同的操作系统而言,从用户态的接口开始到进行最终的SOC编程接口调用的过程中,会经过不同的软件层次。对于Linux操作系统,设备驱动的接口调用过程就是Linux设备驱动框架所决定的。详见<<Linux字符设备驱动剖析>>、<<Linux设备文件的创建和mdev>>、<<总线、设备和驱动>>、<<字符设备驱动、设备驱动模型、sysfs、平台设备驱动的关系>>从上面的分析可以看出,任何Linux设备驱动都有两个层次,一个是偏底层硬件的SOC寄存器编程,一个是偏上层应用的Linux子系统软件接口,前者负责和硬件交互,后者负责跟上层应用交互。Linux为了给用户提供统一的编程接口,在所有的设备驱动上再假设一层公共接口层,如所有驱动都可以通过open、read、write来进行操作,其是Linux设备驱动框架的组成部分,因此一般地,Linux设备驱动都有三个层次。

当然,各个子系统内部还会通过分层来解耦内部的需求和实现

Linux Framebuffer的软件需求

Linux Framebuffer的需求就是驱动LCD屏显示。所以其自然有2个层次,偏底层硬件的SOC寄存器编程和偏上层应用的写屏接口。本文的重点是分析Linux Framebuffer驱动的偏上层应用的接口实现,而不是阐述如何进行SOC编程,因为SOC编程是针对某个具体的SOC寄存器来进行的。

 

  1. LCD屏的驱动需求
    1.  SOC和LCD屏的连接示意图如下:
    2. 总结
      1.SOC编程是为了支持多种不同的LCD屏,以使该设备的应用场景最大化。因此SOC的多媒体Display模块需要考虑
      不同的分辨率、位图深度、行切换和帧切换的延时等。这些参数的设置最终使得LCD控制器(硬件引擎)产生匹配的
      时钟和数据输出到LCD引脚上。这些设置的编程方式和其他字符设备(如鼠标、串口)都是差不多。所以LCD的驱动
      需求是通过寄存器设置支持各种不同的LCD屏。
      2.唯一有点不同就是,LCD驱动器内部有个大的FIFO(和分辨率有关,可能是几百k,甚至是上M字节级)。FIFO中
      存放着LCD屏的显示数据,LCD驱动器内部显示电路会自动将FIFO的数据刷新到LCD屏上。由于FIFO很大,通过CPU
      写总线的方式来讲内存数据写道FIFO的方式是不可行的,这样会加重CPU的负担。现代高级SOC处理器都是用DMA的
      方式,由DMA直接将内存数据搬到FIFO。DMA可以理解为专职搬运工,与CPU、GPU一样是独立工作的,只需要告诉
      它源地址、目的地址和长度就可以了。源地址就是物理内存地址,目的地址就是FIFO的映射地址。DMA工作不经过
      MMU,所以它之人物理内存地址。
      
      那么,LCD的另一个驱动需求就是CPU将用户数据写道DMA所认得物理内存地址上。CPU写好用户数据到物理内存地
      址上,显示专用DMA就自动搬运到显示FIFO上。
  2. LCD屏驱动得实现思路
        1.从上面得分析可以看到,LCD屏的设备驱动可以是一个字符设备驱动。第一个需求通过寄存器设置支持各种不同得LCD屏是很容易
    实现的。第二个需求通过write接口也是很容易实现的。
    
        2.写接口就是将用户图像数据(0-3G进程虚拟空间buffer,对应实际的物理内存地址1)拷贝的内核虚拟地址空间(对应实际的物
    理内存地址2)
    
        3.一般地,我们在用户进程中申请一块物理连续的内存块(返回地址是0-3G的进程虚拟地址空间),并将多个图像资源数据(如
    文字、图像)放入这个内存中。我们把这次资源数据拷贝到内存块称为一次拷贝。然后通过写接口拷贝到实际的显示物理内存,称为
    二次拷贝。
    
        4.虚拟地址空间核物理地址空间的映射是通过MMU(内存管理单元)来进行映射和管理的。MMU机制请看<<SOC嵌入式软件架构
    设计之二:内存管理单元的软、硬件协同设计>>。简单的理解就是程序运行的空间是4G虚拟地址空间,二十集的物理内存可能是1G
    内存,代码和数据是真正存储在实际物理内孙上的。如何通过虚拟地址找到对应的实际物理内存地址就是MMU的作用。

     

  3. Framebuffer的软件需求

从上面分析,图像数据显示要经历两次拷贝。那么有没有方法做到一次拷贝就可以显示了呢?很好,Framebuffer就是利用MMU机制来实现一次拷贝即可显示。

它的显示示意图是:

可见,当用户图像数据buffer和内存虚拟地址空间buffer对应的都是同一块物理内存。当资源数据拷贝到用户推昂数据buffer时,即是直接
拷贝到显示物理内存了。所以,framebuffer驱动最重要的功能就是 给用户提供一个进程空间映射到实际的显示物理内存接口(mmap).它跟
进程间通信的共享变量的原理是一致的。另外,考虑到一台设备可能要支持多个输出,例如HDMI接口、VGA、或者类似视频监控需求,一个屏
幕上有好多个监控窗口。如何更好的管理多个显示缓存。Framebuffer再内部进行了抽象,即其向上层应用同意抽象为一个字符主设备,而再
不同的窗口显示缓存即视为不同的字符从设备。Framebuffer支持多大32个从设备。接下来会从代码级详细分析Linux Framebuffer驱动的
框架组成、mmap和其他接口实现、接口使用场景。透彻理解以上framebuffer驱动的软件需求,再来跟踪分析Linux Framebuffer驱动是
不难的。

示例分析

iTop4412为例

  1. 子系统适配层初始化
    itop4412_kernel_3.0/drivers/video/fbmem.c							
    
    作用:
        1./proc/下创建子系统目录/proc/fb
        2./sys下创建子系统目录类/sys/class/graphics
        3.创建并初始化字符设备fb(主设备号、设备操作集),当前操作集为一个适配层接口,最终通过子设备好重定向到具体的子设备驱
    动操作集
        4.提供具体子设备注入子系统的操作接口:
            注册:register_framebuffer(struct fb_info *fb_info)
            注销:unregister_framebuffer(struct fb_info *fb_info)
  2. 设备添加:
    1. 重要结构体分析
      iTop4412_Kernel_3.0/arch/arm/plat-s5p/dev-fimd-s5p.c
      struct platform_device s3c_device_fb = {                    // fb设备定义和描述
      	.name		        = "s3cfb",
      #if defined(CONFIG_ARCH_EXYNOS4)
      	.id		            = 0,
      #else
      	.id		            = -1,
      #endif
      	.num_resources	    = ARRAY_SIZE(s3cfb_resource),
      	.resource	        = s3cfb_resource,            // 指定上面的资源
      	.dev		        = {
      	.dma_mask		    = &fb_dma_mask,
      	.coherent_dma_mask	= 0xffffffffUL
      	}
      };
      static struct s3c_platform_fb default_fb_data __initdata = {
      #if defined(CONFIG_ARCH_EXYNOS4)
      	.hw_ver	            = 0x70,                        // 硬件版本号
      #else
      	.hw_ver	            = 0x62,
      #endif
      	.nr_wins	        = 5,                       // 支持的窗口数量-----如何运用?
      #if defined(CONFIG_FB_S5P_DEFAULT_WINDOW)
      	.default_win	    = CONFIG_FB_S5P_DEFAULT_WINDOW,
      #else
      	.default_win	    = 0,
      #endif
      	.swap		    = FB_SWAP_WORD | FB_SWAP_HWORD,// 数据子交换
      };
  3. 驱动添加:
    1. 分层:
      drivers/video/fbmem.c:        设备系统层,子设备集和内核的适配层,此为子系统的主要作用
          drivers/video/s3cfb_main.c:   设备驱动层,主要为子设备集
          drivers/video/s3cfb_ops.c:    设备应用功能的逻辑代码实现
          drivers/video/s3cfb_fimd6x.c: 设备硬件操作的原始接口实现---即读写寄存器元接口封装
    2. 流程分析:

      static struct platform_driver s3cfb_driver = {
          .probe		= s3cfb_probe,
          .remove		= s3cfb_remove,
      #ifndef CONFIG_HAS_EARLYSUSPEND
          .suspend	= s3cfb_suspend,
          .resume		= s3cfb_resume,
      #endif
          .driver		= {
              .name	= "s3cfb",
              .owner	= THIS_MODULE,
      #ifdef CONFIG_EXYNOS_DEV_PD
              .pm	= &s3cfb_pm_ops,
      #endif
          },
      };
      
      platform_driver_register(&s3cfb_driver);
          ......
          平台总线设备匹配过程,匹配成功进入子设备驱动的probe
          ......
          s3cfb_probe
              获取平台设备devices的硬件资源
              申请软件资源
              映射硬件资源
              硬件初始化	:
                  中断初始化
                  设备硬件初始化:
                  配置LCD输出模式如RGB等
                  配置行列刷新模式,刷新控制时钟频率
                  配置屏幕大小
              申请并注册framebuffer
              使能设备
              创建属性文件
                  申请framebuffer:
                  itop4412_kernel_3.0/drivers/video/s3cfb_main.c
                      s3cfb_alloc_framebuffer(fbdev[i], i)
                          for (i = 0; i < pdata->nr_wins; i++) {
                          fbdev->fb[i] = framebuffer_alloc(sizeof(struct s3cfb_window),fbdev->dev);
                          s3cfb_init_fbinfo(fbdev, i);
                              fb资源初始化
                              fb->fbops = &s3cfb_ops;					//绑定具体设备操作集
                          }	
                      s3cfb_register_framebuffer(fbdev[i], i)
                          ......
                          for (i = pdata->default_win; i < pdata->nr_wins + pdata->default_win; i++) {
                              j = i % pdata->nr_wins;
                              ret = register_framebuffer(fbdev->fb[j]);
                          }
                              register_framebuffer
                                  do_register_framebuffer(fb_info);
                                      for (i = 0 ; i < FB_MAX; i++)
                                          if (!registered_fb[i])
                                              break;
                                          device_create(fb_class, NULL,MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
                                          fb_info->dev = device_create(fb_class, fb_info->device,MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
                                          registered_fb[i] = fb_info;

       

  4. 应用访问子设备
    root@android:/ # ls /dev/graphics/fb*
    fb0  fb1  fb10 fb11 fb2  fb3  fb4  fb5  fb6  fb7  fb8  fb9
    
    Notes:
    子系统存在的意义:
        将同类设备归集提取共性,同时进行统一管理,提供具体设备集至内核的一个适配层
    问:如何统一管理
    答:提供一个结构体作为所有子设备的一个超集,具体设备具体初始化,然后将所有设备对应的结构体采用数组统一管理
    问:子设备驱动操作集绑定在fb->fops下,用户通过设备名路径访问时内核如何定位至具体设备的驱动操作集
    答:所有子设备共享一个主设备号,在子系统是个虚拟的软件资源,没有硬件做支撑,但文件系统设备管理的字符设备驱动集chrdevs是
    个纯软件的东西,也可以注册并占用一个主设备号,同时将字符设备驱动集绑定进其成员

     

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值