Linux驱动:基于framebuffer(fb)的驱动分析

基于framebuffer的驱动分析

framebuffer帧缓冲(简称fb)是linux内核中用代码虚拟出的一个设备,是一个platform类型设备,设备文件位于/dev/fb*

  • 在嵌入式系统中一般没有专门的显存,而仅仅是从RAM(SDRAM)空间中分配一段显示缓冲区
  • framebuffer的作用是:向应用层提供一个统一标准接口的显示设备。不论最终输出是通过hdmi还是lcd控制器,可以认为所有的GUI都是向fb输出画面的
  • 实际上是frambuffer就是linux内核驱动申请的一片内存空间,然后lcd内有一片sram,cpu内部有个lcd控制器,它有个单独的dma用来将frambuffer中的数据拷贝到lcd的sram中去,拷贝到lcd的sram中的数据就会显示在lcd上,具体数据的内容是由应用程序控制的。
  • LCD驱动和framebuffer驱动没有必然的联系,它只是驱动LCD正常工作的,比如有信号传过来,那么LCD驱动负责把信号转成显示屏上的内容,至于什么内容,怎么显示,它根本不关心也不知道。
  • 对于现代LCD,有一种“多屏叠加”的机制,即一个LCD设备可以有多个独立虚拟屏幕,以达到画面叠加的效果。所以fb与LCD不是一对一的关系,在常见的情况下,一个LCD对应了fb0~fb4。像QT这种GUI会默认把画面输出到fb0

1.画面输出原理

  • 如何输出画面?应用程序通过往显存中写数据,LCD控制器将自动把显存中的数据映射到lcd屏幕。映射是全自动的,应用层负责往显存里写数据,而驱动要做的仅仅是配置LCD控制器、创建显存罢了
  • 由于显存实际是处于内核态的物理内存,所以要把这块物理内存映射到用户态,所谓“映射”就可以理解为建立了一个“符号链接”,这样应用程序就可以直接操作这块物理内存了
  • 关于LCD的硬件原理详见LCD简介
  • 关于如何在应用层测试fb,详见framebuffer的使用与测试

2.framebuffer驱动结构



fb的结构和misc极为类似,由内核中的fb框架实现一部分,然后再由设备驱动本身实现一部分。设备驱动本身就是一个普通的platform总线驱动 
虽然每家原厂写的fb设备驱动可能有些差异,但是基本的套路还是相同的 
这里写图片描述 
这里写图片描述

  • 在内核fb框架中,所有的fb设备公用一个主设备号(都是29),它们之间以次设备号互相区分。所以在框架中使用register_chrdev注册了一个主设备号为29的设备,而在驱动中device_create创建设备文件主设备号都为29,次设备号不同
  • 由上图可以看出,内核提供了fb框架,原厂提供了fb的platform设备和驱动;不论有多少LCD屏幕,用的都是这一套platform驱动,它们的操作方式都是固定的,唯一的区别就在platform_data里的硬件参数。而我们驱动工程师重点关注的就是该硬件参数

3.修改LCD的硬件参数(2.6版本内核)

当我们板子上的LCD需要更换时,驱动中也需要进行相应的修改

  • 具体的思路,是去修改platform_device中的platform_data,LCD的硬件参数都在里面,但是怎么找到这个platform_data是一门学问,因为原厂写的代码是比较复杂的。。。。具体方法详见基于platform总线的驱动分析 的文末
  • 不难找到设置platform_data的地方,如图 
    这里写图片描述 
    那么目前导入的platform_data是哪一个呢?根据menuconfig和xxxxdefconfig,分析可知是ek070tn93_fb_data
static struct s3c_platform_fb ek070tn93_fb_data __initdata = {
    .hw_ver = 0x62,
    .nr_wins = 5,
    .default_win = CONFIG_FB_S3C_DEFAULT_WINDOW,
    .swap = FB_SWAP_WORD | FB_SWAP_HWORD,
 
    .lcd = &ek070tn93,
    .cfg_gpio   = ek070tn93_cfg_gpio,
    .backlight_on   = ek070tn93_backlight_on,
    .backlight_onoff    = ek070tn93_backlight_off,
    .reset_lcd  = ek070tn93_reset_lcd,
};
  • 里面最关键的是.lcd 这个成员,即结构体ek070tn93,查看发现里面有时序、分辨率等各种参数,这样我们就可以随便修改参数了
static struct s3cfb_lcd ek070tn93 = {
    .width = S5PV210_LCD_WIDTH,
    .height = S5PV210_LCD_HEIGHT,
    .bpp = 32,
    .freq = 60,
 
    .timing = {
        .h_fp   = 210,
        .h_bp   = 38,
        .h_sw   = 10,
        .v_fp   = 22,
        .v_fpe  = 1,
        .v_bp   = 18,
        .v_bpe  = 1,
        .v_sw   = 7,
    },
    .polarity = {
        .rise_vclk = 0,
        .inv_hsync = 1,
        .inv_vsync = 1,
        .inv_vden = 0,
    },
};

4.修改LCD的硬件参数(3.0+版本内核)

对于新的内核,platformdata都包含在了dts中,所以需要在dts中修改LCD的硬件参数。有关设备树详见设备树详解

  • 首先进入我们项目的dts以及包含的dtsi,寻找合适的地方安放我们新增的时序,一般某个dtsi里有一个叫display-timings的节点,里面会放时序。其实说实话时序放在哪里根本就无所谓,因为lcd/ldb节点是通过标号来访问具体的时序节点的,我们之所以选择放在display-timings里,仅仅是为了规范一点
display-timings {
 
        lq4851lg03:lvds_1280x480_53M{
            clock-frequency = <53172000>;
            hactive = <1280>;
            vactive = <480>;
            hback-porch = <268>;
            hfront-porch = <70>;
            vback-porch = <10>;
            vfront-porch = <10>;
            hsync-len = <70>;
            vsync-len = <25>;
            pixelclk-active = <0>;
        };
 
        ak070tn93:ttl_1280x480_45M{
            clock-frequency = <45000000>;
            hactive = <1280>;
            vactive = <480>;
            hback-porch = <40>;
            hfront-porch = <73>;
            vback-porch = <20>;
            vfront-porch = <23>;
            hsync-len = <20>;
            vsync-len = <10>;
        };
        xxxxx:xxxxx{
 
        /*需要添加的参数*/
 
        };
    };
  • 那么我们有两种方案,第一种是直接在默认的时序上修改,第二种添加一个时序,并在项目的dts中使用该时序。在此,我们选择第二种更优越的方法
  • 首先在已有的时序后面添加一个时序,具体参数照抄datasheet即可,唯一要注意的是某几个参数的名字可能和datasheet上不同:hsync-len对应datasheet上的Horizontal pulse width;vsync-len 对应datasheet上的Vertical pulse width。那么上面代码中的pixelclk-active = <0>;意味着什么呢?这个元素代表时钟的极性,如果显示图片清晰度不足时,可以尝试加入该元素
  • 打开我们项目的dts,然后可以做如下的修改
&mxcfb1 {
    disp_dev = "lcd";
};
 
&ldb {
    status = "disabled";
};
 
&lcd {
    status = "okay";
    native-mode = <&xxxx>;
};
  • 可以看到有三项,mxcfb1、ldb、lcd。mxcfb1里面的disp_dev决定了图像通过什么方式输出,显然这里有两种方式,ldb和lcd,即lvds输出或ttl输出RGB信号。这里我们选择了lcd(即ttl输出RGB信号),那么ldb显然是要被”disabled”了,而lcd显然是”okay”,并且lcd的native-mode选择了我们刚刚添加的时序
  • 有时,除了时序之外,显示的模式可能也需要设置,比如某个屏幕需要jeida的data-mapping模式,而我们项目中的dtsi中并未设置过:

ldb: ldb@020e0008 {
    #address-cells = <1>;
    #size-cells = <0>;
    gpr = <&gpr>;
    status = "disabled";
 
    lvds-channel@0 {
        reg = <0>;
        status = "disabled";
    };
  • 那么在我们项目的dts中就要对其进行引用,并设置(类似于重写)
&ldb {
    status = "okay";
    lvds-channel@0 {
        native-mode = <&SHARP_LQ123B5LW>;
        fsl,data-mapping = "jeida";
        status = "okay";
    };
};

5.修改logo显示(2.6版本内核)

注意:本段关于logo显示的ne 
当kernel启动,在probe函数运行时,一般会往fb中输出一个小企鹅logo(开发板厂商可能会改成其他的)。很多时候产品是不需要这个企鹅logo的,我们要学会去修改它

  • 在probe函数中,我们可以发现调用了显示logo的相关代码,下面是三星写的,其他厂家应该也不会有很大的区别:
#if !defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_LOGO)
    if (fb_prepare_logo( fbdev->fb[pdata->default_win], FB_ROTATE_UR)) {
        printk("Start display and show logo\n");
        /* Start display and show logo on boot */
        fb_set_cmap(&fbdev->fb[pdata->default_win]->cmap, fbdev->fb[pdata->default_win]);
        fb_show_logo(fbdev->fb[pdata->default_win], FB_ROTATE_UR);
    }
#endif
  • 不难发现如果要让logo消失,只需要让CONFIG_LOGO不被定义即可,即在menuconfig中配置,或者直接修改xxxdefconfig
  • 那么如果我们想要添加另外的logo呢?linux的启动logo全部放在drivers/video/logo/下,而且都是以一种专门的格式存在的,称之为ppm格式。我们可以使用专门的工具把png格式图片转成ppm格式,ubuntu里也有这个工具,网上教程很多
  • 假设添加完了,我们要选择该logo,怎么选择?很简单,这些logo也是由kconfig管理的,我们只要在drivers/video/logo/下的kconfig和makefile中照葫芦画瓢,添加我们的logo,然后就能在menuconfig中选择了!
  • 有时,我们做了一个很小的logo,比屏幕像素小得多,它会被系统放到屏幕左上角。那么如何把它放到屏幕正中央呢?有两种思路,第一种方法是把logo的整个画面做的和屏幕分辨率一样大小,这样自然就对齐到中间了;第二种方法是修改显示logo函数的参数。首先找到probe函数中的fb_show_logo,在进去一层,找到fb_show_logo_line,查看其定义,发现里面有两行:
image.dx = 0;
image.dy = y;

这便是logo图像的坐标偏移量,只需改成恰当的值即可让logo显示到中央了


/*********************************************************************************************/

b_fix_screeninfo 和 fb_var_screeninfo



fb_fix_screeninfo 和 fb_var_screeninfo 都和 frame buffer 有关。

结构体的成员变量

[cpp]  view plain   copy
  print ?
  1. struct fb_fix_screeninfo {  
  2.     char id[16];            /* identification string eg "TT Builtin" */  
  3.     unsigned long smem_start;   /* Start of frame buffer mem */  
  4.                     /* (physical address) */  
  5.     __u32 smem_len;         /* Length of frame buffer mem */  
  6.     __u32 type;         /* see FB_TYPE_*        */  
  7.     __u32 type_aux;         /* Interleave for interleaved Planes */  
  8.     __u32 visual;           /* see FB_VISUAL_*      */   
  9.     __u16 xpanstep;         /* zero if no hardware panning  */  
  10.     __u16 ypanstep;         /* zero if no hardware panning  */  
  11.     __u16 ywrapstep;        /* zero if no hardware ywrap    */  
  12.     __u32 line_length;      /* length of a line in bytes    */  
  13.     unsigned long mmio_start;   /* Start of Memory Mapped I/O   */  
  14.                     /* (physical address) */  
  15.     __u32 mmio_len;         /* Length of Memory Mapped I/O  */  
  16.     __u32 accel;            /* Indicate to driver which */  
  17.                     /*  specific chip/card we have  */  
  18.     __u16 reserved[3];      /* Reserved for future compatibility */  
  19. };  

结构体的成员变量

[cpp]  view plain   copy
  print ?
  1. struct fb_var_screeninfo {  
  2.     __u32 xres;         /* visible resolution       */  
  3.     __u32 yres;  
  4.     __u32 xres_virtual;     /* virtual resolution       */  
  5.     __u32 yres_virtual;  
  6.     __u32 xoffset;          /* offset from virtual to visible */  
  7.     __u32 yoffset;          /* resolution           */  
  8.   
  9.     __u32 bits_per_pixel;       /* guess what           */  
  10.     __u32 grayscale;        /* != 0 Graylevels instead of colors */  
  11.   
  12.     struct fb_bitfield red;     /* bitfield in fb mem if true color, */  
  13.     struct fb_bitfield green;   /* else only length is significant */  
  14.     struct fb_bitfield blue;  
  15.     struct fb_bitfield transp;  /* transparency         */    
  16.   
  17.     __u32 nonstd;           /* != 0 Non standard pixel format */  
  18.   
  19.     __u32 activate;         /* see FB_ACTIVATE_*        */  
  20.   
  21.     __u32 height;           /* height of picture in mm    */  
  22.     __u32 width;            /* width of picture in mm     */  
  23.   
  24.     __u32 accel_flags;      /* (OBSOLETE) see fb_info.flags */  
  25.   
  26.     /* Timing: All values in pixclocks, except pixclock (of course) */  
  27.     __u32 pixclock;         /* pixel clock in ps (pico seconds) */  
  28.     __u32 left_margin;      /* time from sync to picture    */  
  29.     __u32 right_margin;     /* time from picture to sync    */  
  30.     __u32 upper_margin;     /* time from sync to picture    */  
  31.     __u32 lower_margin;  
  32.     __u32 hsync_len;        /* length of horizontal sync    */  
  33.     __u32 vsync_len;        /* length of vertical sync  */  
  34.     __u32 sync;         /* see FB_SYNC_*        */  
  35.     __u32 vmode;            /* see FB_VMODE_*       */  
  36.     __u32 rotate;           /* angle we rotate counter clockwise */  
  37.     __u32 reserved[5];      /* Reserved for future compatibility */  
  38. };  

fb_fix_screeninfo 的 line_length 成员,含义是一行的 size,以字节数表示,就是屏幕的宽度。

结 构fb_var_screeninfo定义了视频硬件一些可变的特性。这些特性在程序运行期间可以由应用程序动态改变


6.在LCD上划线

***************************************************************************************************************************************
[cpp]  view plain   copy
  print ?
  1. #include <stdlib.h>  
  2. #include <unistd.h>  
  3. #include <stdio.h>  
  4. #include <fcntl.h>  
  5. #include <linux/fb.h>  
  6. #include <linux/kd.h>  
  7. #include <sys/mman.h>  
  8. #include <sys/ioctl.h>  
  9. #include <sys/time.h>  
  10. #include <string.h>  
  11. #include <errno.h>  
  12. struct fb_var_screeninfo vinfo;  
  13. struct fb_fix_screeninfo finfo;  
  14. char *frameBuffer = 0;  
  15.    
  16. //打印fb驱动中fix结构信息,注:在fb驱动加载后,fix结构不可被修改。  
  17. void printFixedInfo ()  
  18. {  
  19.    printf ("Fixed screen info:\n"  
  20.                         "\tid: %s\n"  
  21.                         "\tsmem_start:0x%lx\n"  
  22.                         "\tsmem_len:%d\n"  
  23.                         "\ttype:%d\n"  
  24.                         "\ttype_aux:%d\n"  
  25.                         "\tvisual:%d\n"  
  26.                         "\txpanstep:%d\n"  
  27.                         "\typanstep:%d\n"  
  28.                         "\tywrapstep:%d\n"  
  29.                         "\tline_length: %d\n"  
  30.                         "\tmmio_start:0x%lx\n"  
  31.                         "\tmmio_len:%d\n"  
  32.                         "\taccel:%d\n"  
  33.            "\n",  
  34.            finfo.id, finfo.smem_start, finfo.smem_len, finfo.type,  
  35.            finfo.type_aux, finfo.visual, finfo.xpanstep, finfo.ypanstep,  
  36.            finfo.ywrapstep, finfo.line_length, finfo.mmio_start,  
  37.            finfo.mmio_len, finfo.accel);  
  38. }  
  39.    
  40. //打印fb驱动中var结构信息,注:fb驱动加载后,var结构可根据实际需要被重置  
  41. void printVariableInfo ()  
  42. {  
  43.    printf ("Variable screen info:\n"  
  44.                         "\txres:%d\n"  
  45.                         "\tyres:%d\n"  
  46.                         "\txres_virtual:%d\n"  
  47.                         "\tyres_virtual:%d\n"  
  48.                         "\tyoffset:%d\n"  
  49.                         "\txoffset:%d\n"  
  50.                         "\tbits_per_pixel:%d\n"  
  51.                         "\tgrayscale:%d\n"  
  52.                         "\tred: offset:%2d, length: %2d, msb_right: %2d\n"  
  53.                         "\tgreen: offset:%2d, length: %2d, msb_right: %2d\n"  
  54.                         "\tblue: offset:%2d, length: %2d, msb_right: %2d\n"  
  55.                         "\ttransp: offset:%2d, length: %2d, msb_right: %2d\n"  
  56.                         "\tnonstd:%d\n"  
  57.                         "\tactivate:%d\n"  
  58.                         "\theight:%d\n"  
  59.                         "\twidth:%d\n"  
  60.                         "\taccel_flags:0x%x\n"  
  61.                         "\tpixclock:%d\n"  
  62.                         "\tleft_margin:%d\n"  
  63.                         "\tright_margin: %d\n"  
  64.                         "\tupper_margin:%d\n"  
  65.                         "\tlower_margin:%d\n"  
  66.                         "\thsync_len:%d\n"  
  67.                         "\tvsync_len:%d\n"  
  68.                         "\tsync:%d\n"  
  69.                        "\tvmode:%d\n"  
  70.            "\n",  
  71.            vinfo.xres, vinfo.yres, vinfo.xres_virtual, vinfo.yres_virtual,  
  72.            vinfo.xoffset, vinfo.yoffset, vinfo.bits_per_pixel,  
  73.            vinfo.grayscale, vinfo.red.offset, vinfo.red.length,  
  74.             vinfo.red.msb_right,vinfo.green.offset, vinfo.green.length,  
  75.            vinfo.green.msb_right, vinfo.blue.offset, vinfo.blue.length,  
  76.            vinfo.blue.msb_right, vinfo.transp.offset, vinfo.transp.length,  
  77.            vinfo.transp.msb_right, vinfo.nonstd, vinfo.activate,  
  78.            vinfo.height, vinfo.width, vinfo.accel_flags, vinfo.pixclock,  
  79.            vinfo.left_margin, vinfo.right_margin, vinfo.upper_margin,  
  80.            vinfo.lower_margin, vinfo.hsync_len, vinfo.vsync_len,  
  81.            vinfo.sync, vinfo.vmode);  
  82. }  
下面才是我们的重点,这个代码是我自己参考别人画矩形的代码改过来的
[cpp]  view plain   copy
  print ?
  1. //画一条直线  
  2. void drawline_rgb16 (int x0,int y0, int width,int height, int color,int flag0)  
  3. {  
  4.    const int bytesPerPixel = 2;//因为是rgb16,用16位来描述色深,所以2个字节  
  5.    const int stride = finfo.line_length / bytesPerPixel;,一行有多少个点  
  6.    const int red = (color & 0xff0000) >> (16 + 3);//下面是颜色的操作,我目前还没弄明白  
  7.    const int green = (color & 0xff00) >> (8 + 2);  
  8.    const int blue = (color & 0xff) >> 3;  
  9.    const short color16 = blue | (green << 5) | (red << (5 +6));  
  10.    int flag=flag0;//这里我为了图个方便就用一个flag来区分是画横线还是竖线,0表示横线,1表示竖线。  
  11.   
  12.    
  13.    short *dest = (short *) (frameBuffer)+ (y0 + vinfo.yoffset) * stride + (x0 +vinfo.xoffset);//这个就是我们画点的起始位置,其+stride就是换行(这个是我个人通过代码测试得出来的结论)  
  14.     
  15.    int x=0,y=0;  
  16.    if(flag==0)  
  17.    {       
  18.    for (x = 0; x < width; ++x)//width就是我们x方向的终点  
  19.        {  
  20.            dest[x] = color16;  
  21.        }  
  22.    }  
  23.    else if(flag==1)  
  24.    {  
  25.        for(y=0;y<height;y++)//height就是我们y方向的终点  
  26.        {  
  27.            dest[x]=color16;//这里x始终为0,和下面一句结合起来就是每一行就画一个点,一共画height行,不就是一条竖线了么,这里我还思考了很久。  
  28.            
  29.            dest +=stride;  
  30.        }  
  31.    }  
  32. }  
解释:我的屏的lcd分辨率是480*272,分辨率的意思是一行有480个点,一共有272行,其实屏蔽上都是一个个点组成的,在上面画线的意思并不是真正意思上的拿一支笔画线。打个比方来说你你把一行中80-180个点都改成红色(我们屏蔽不是黑色么),改完你就可以看见一条红线了,感觉就是画了一条红色的直线对不对?

而且“上色”是从左到右一个点一个点,一行一行“上色”的,屏幕的坐标系如下图所示:

[cpp]  view plain   copy
  print ?
  1. short *dest = (short *) (frameBuffer)+ (y0 + vinfo.yoffset) * stride + (x0 +vinfo.xoffset);  
上面这一行代码的具体意思就是定位到(x0,y0)这个坐标,也就是我们要画的其实位置

可以下面这个代码画一个矩形。
[cpp]  view plain   copy
  print ?
  1. //画大小为width*height的同色矩阵,5reds+6greens+5blues  
  2. void drawRect_rgb16 (int x0, int y0, int width,int height, int color)  
  3. {  
  4.    const int bytesPerPixel = 2;  
  5.    const int stride = finfo.line_length / bytesPerPixel;  
  6.    const int red = (color & 0xff0000) >> (16 + 3);  
  7.    const int green = (color & 0xff00) >> (8 + 2);  
  8.    const int blue = (color & 0xff) >> 3;  
  9.    const short color16 = blue | (green << 5) | (red << (5 +6));  
  10.    
  11.    short *dest = (short *) (frameBuffer)+ (y0 + vinfo.yoffset) * stride + (x0 +vinfo.xoffset);  
  12.    
  13.    int x, y;  
  14.    for (y = 0; y < height; ++y)  
  15.     {  
  16.        for (x = 0; x < width; ++x)  
  17.        {  
  18.            dest[x] = color16;  
  19.        }  
  20.        dest += stride;  
  21.     }  
  22. }  
下面是main函数:

[cpp]  view plain   copy
  print ?
  1. int main (int argc, char **argv)  
  2. {  
  3.    const char *devfile = "/dev/fb0";  
  4.    long int screensize = 0;  
  5.    int fbFd = 0;  
  6.   
  7.   
  8.    
  9.    /* Open the file for reading and writing */  
  10.    fbFd = open (devfile, O_RDWR);  
  11.    if (fbFd == -1)  
  12.     {  
  13.        perror ("Error: cannot open framebuffer device");  
  14.        exit (1);  
  15.     }  
  16.    
  17.    //获取finfo信息并显示  
  18.    if (ioctl (fbFd, FBIOGET_FSCREENINFO, &finfo) == -1)  
  19.     {  
  20.        perror ("Error reading fixed information");  
  21.        exit (2);  
  22.     }  
  23.    printFixedInfo ();  
  24.    //获取vinfo信息并显示  
  25.    if (ioctl (fbFd, FBIOGET_VSCREENINFO, &vinfo) == -1)  
  26.     {  
  27.        perror ("Error reading variable information");  
  28.        exit (3);  
  29.     }  
  30.    printVariableInfo ();  
  31.    
  32.    /* Figure out the size of the screen in bytes */  
  33.    screensize = finfo.smem_len;//fb的缓存长度  
  34.    
  35.    /* Map the device to memory */  
  36.     frameBuffer =(char *) mmap (0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED,fbFd, 0);  
  37.     if (frameBuffer == MAP_FAILED)  
  38.        {  
  39.            perror ("Error: Failed to map framebuffer device to memory");  
  40.            exit (4);  
  41.        }  
  42.     
  43.        //drawRect_rgb16 (vinfo.xres *3 / 8, vinfo.yres * 3 / 8,vinfo.xres / 4, vinfo.yres / 4,0xff00ff00);//实现画矩形  
  44.   
  45.        drawline_rgb16(50,80,260,0,0xffff0000,0);  
  46.          
  47.        drawline_rgb16(160,10,0,180,0xff00ff00,1);//可以画出一个交叉的十字,坐标都是自己设的。  
  48.        sleep (2);  
  49.        printf (" Done.\n");  
  50.    
  51.        munmap (frameBuffer, screensize);   //解除内存映射,与mmap对应  
  52.    
  53.        close (fbFd);  
  54.        return 0;  
  55. }  






用一个流程图还说明一下mian函数吧
*****************************************************************************************************************************************************************************************
****************************************************************************************************************************************************************************************
这里最重要的就是mmap这个函数了

void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);

addr指定文件应被映射到进程空间的起始地址,一般被指定一个空指针,此时选择起始地址的任务留给内核来完成。函数的返回值为最后文件映射到进程空间的地址,进程可直接操作起始地址为该值的有效地址。
    len是映射到调用进程地址空间的字节数,它从被映射文件开头offset个字节开始算起。
    prot参数指定共享内存的访问权限。可取如下几个值的或:PROT_READ(可读),PROT_WRITE(可写),PROT_EXEC(可执行),PROT_NONE(不可访问)。
   flags由以下几个常值指定:MAP_SHARED, MAP_PRIVATE, MAP_FIXED。其中,MAP_SHARED,MAP_PRIVATE必选其一,而MAP_FIXED则不推荐使用。
 如果指定为MAP_SHARED,则对映射的内存所做的修改同样影响到文件。如果是MAP_PRIVATE,则对映射的内存所做的修改仅对该进程可见,对文件没有影响。
    offset参数一般设为0,表示从文件头开始映射。

mmap使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再调用read(),write()等操作。


 再来看看我们代码中的mmap的实例
frameBuffer =(char *) mmap (0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED,fbFd, 0);

前面我们不是说了 frambuffer就是lin ux内核驱动申请的一片内存空间,lcd驱动将frambuffer的地址通过mmap()将这片内存映射到应用程序空间,这样我们写入到fb的数据就写入到内核驱动里的frambuffer中去了,而lcd 的dma就将这些数据写入到lcd的sram中,从而显示在lcd上.


版权声明

本文转自:https://blog.csdn.net/qq_38892883/article/details/77950619

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
买书时赠送的电子稿,代码示例丰富,非常不错!全书分三个文件打包。 linuxdriver_code_tool |-- 03 | `-- 2.6内核升级工具 | |-- device-mapper-1.00.19-2.i386.rpm | |-- lvm2-2.00.25-1.01.i386.rpm | |-- mkinitrd-4.2.0.3.tar.tar | |-- module-init-tools-3.2.2.tar.bz2 | `-- modutils-2.4.5-1.src.rpm |-- 04 | |-- 内核模块参数范例 | | `-- book.c | |-- 内核模块导出符号 | | `-- export_symb.c | `-- 最简单的内核模块 | `-- hello.c |-- 05 | `-- udev源代码 | `-- udev-114.tar.gz |-- 06 | |-- globalmem驱动 | | `-- globalmem.c | `-- 包含2个globalmem设备的驱动 | `-- globalmem_two.c |-- 07 | `-- 含并发控制的globalmem驱动 | `-- globalmem_lock.c |-- 08 | |-- globalfifo驱动 | | `-- globalfifo.c | `-- poll应用程序范例 | `-- pollmonitor.c |-- 09 | |-- 异步通知应用程序范例 | | `-- asyncmonitor.c | `-- 支持异步通知的globalfifo | `-- globalfifo_async.c |-- 10 | |-- S3C2410实时钟驱动 | | `-- s3c2410-rtc.c | `-- 秒设备驱动与应用程序 | |-- second.c | `-- second_test.c |-- 11 | |-- DMA范例 | | |-- 3c505.c | | |-- 3c505.h | | `-- dma.h | `-- 静态映射范例 | `-- mach-smdk2440.c |-- 12 | |-- NVRAM驱动 | | `-- generic_nvram.c | |-- 触摸屏驱动 | | |-- 作为input设备 | | | |-- s3c2410_ts.c | | | `-- s3c2410_ts.h | | `-- 作为普通字符设备 | | `-- s3c2410-ts.c | |-- 看门狗驱动 | | `-- s3c2410_wdt.c | `-- 平台设备 | `-- devs.c |-- 13 | |-- IDE驱动 | | |-- ide-disk.c | | `-- ide-h8300.c | `-- RAMDISK驱动 | `-- rd.c |-- 14 | |-- S3C2410串口驱动 | | |-- regs-gpio.h | | |-- regs-serial.h | | `-- s3c2410.c | `-- 串口核心层 | |-- serial_core.c | `-- serial_core.h |-- 15 | |-- S3C2410 I2C主机驱动 | | |-- i2c-s3c2410.c | | |-- iic.h | | |-- regs-gpio.h | | `-- regs-iic.h | `-- SAA711x I2C设备驱动 | `-- saa711x.c |-- 16 | `-- CS8900以太网设备驱动 | |-- cs89x0.c | `-- cs89x0.h |-- 17 | |-- ALSA工具及库 | | |-- alsa-driver-1.0.15.tar.bz2 | | |-- alsa-firmware-1.0.15.tar.bz2 | | |-- alsa-lib-1.0.15.tar.bz2 | | |--
Linux 设备驱动开发详解 linuxdriver_code_tool |-- 03 | `-- 2.6内核升级工具 | |-- device-mapper-1.00.19-2.i386.rpm | |-- lvm2-2.00.25-1.01.i386.rpm | |-- mkinitrd-4.2.0.3.tar.tar | |-- module-init-tools-3.2.2.tar.bz2 | `-- modutils-2.4.5-1.src.rpm |-- 04 | |-- 内核模块参数范例 | | `-- book.c | |-- 内核模块导出符号 | | `-- export_symb.c | `-- 最简单的内核模块 | `-- hello.c |-- 05 | `-- udev源代码 | `-- udev-114.tar.gz |-- 06 | |-- globalmem驱动 | | `-- globalmem.c | `-- 包含2个globalmem设备的驱动 | `-- globalmem_two.c |-- 07 | `-- 含并发控制的globalmem驱动 | `-- globalmem_lock.c |-- 08 | |-- globalfifo驱动 | | `-- globalfifo.c | `-- poll应用程序范例 | `-- pollmonitor.c |-- 09 | |-- 异步通知应用程序范例 | | `-- asyncmonitor.c | `-- 支持异步通知的globalfifo | `-- globalfifo_async.c |-- 10 | |-- S3C2410实时钟驱动 | | `-- s3c2410-rtc.c | `-- 秒设备驱动与应用程序 | |-- second.c | `-- second_test.c |-- 11 | |-- DMA范例 | | |-- 3c505.c | | |-- 3c505.h | | `-- dma.h | `-- 静态映射范例 | `-- mach-smdk2440.c |-- 12 | |-- NVRAM驱动 | | `-- generic_nvram.c | |-- 触摸屏驱动 | | |-- 作为input设备 | | | |-- s3c2410_ts.c | | | `-- s3c2410_ts.h | | `-- 作为普通字符设备 | | `-- s3c2410-ts.c | |-- 看门狗驱动 | | `-- s3c2410_wdt.c | `-- 平台设备 | `-- devs.c |-- 13 | |-- IDE驱动 | | |-- ide-disk.c | | `-- ide-h8300.c | `-- RAMDISK驱动 | `-- rd.c |-- 14 | |-- S3C2410串口驱动 | | |-- regs-gpio.h | | |-- regs-serial.h | | `-- s3c2410.c | `-- 串口核心层 | |-- serial_core.c | `-- serial_core.h |-- 15 | |-- S3C2410 I2C主机驱动 | | |-- i2c-s3c2410.c | | |-- iic.h | | |-- regs-gpio.h | | `-- regs-iic.h | `-- SAA711x I2C设备驱动 | `-- saa711x.c |-- 16 | `-- CS8900以太网设备驱动 | |-- cs89x0.c | `-- cs89x0.h |-- 17 | |-- ALSA工具及库 | | |-- alsa-driver-1.0.15.tar.bz2 | | |-- alsa-firmware-1.0.15.tar.bz2 | | |-- alsa-lib-1.0.15.tar.bz2 | | |-- alsa-oss-1.0.15.tar.bz2 | | |-- alsa-tools-1.0.15.tar.bz2 | | |-- alsa-utils-1.0.13.tar.bz2 | | `-- pyalsa-1.0.15.tar.bz2 | |-- ALSA驱动范例 | | |-- sa11xx-uda1341.c | | `-- uda1341.h | |-- ALSA应用程序范例 | | |-- pcm.c | | `-- pcm_min.c | |-- OSS驱动范例 | | `-- s3c2410-uda1341.c | `-- OSS应用程序范例 | |-- mixer.c | `-- sound.c |-- 18 | |-- FRAMEBUFFER应用程序范例 | | `-- fb_display | | |-- fb_display.c | | |-- fb_display.h | | |-- Makefile | | |-- README | | `-- test.c | `-- S3C2410 LCD驱动 | |-- s3c2410fb.c | `-- s3c2410fb.h |-- 19 | |-- busybox源代码 | | `-- busybox-1.2.1.tar.bz2 | |-- MTD工具 | | `-- mtd-utils-1.0.0.tar.gz | |-- nand驱动范例 | | `-- s3c2410.c | |-- nor驱动范例 | | `-- s3c2410nor.c | `-- yaffs&yaffs2源代码 | |-- yaffs.tar.gz | `-- yaffs2.tar.gz |-- 20 | |-- USB串口驱动 | | |-- usb-serial.c | | `-- usb-serial.h | |-- USB工具 | | `-- usbview-1.0.tar.tar | |-- USB骨架程序 | | `-- usb-skeleton.c | |-- USB键盘驱动 | | |-- input.h | | |-- usb_input.h | | `-- usbkbd.c | `-- usb主机控制器驱动范例 | |-- ohci-s3c2410.c | `-- usb-control.h |-- 21 | |-- PCI骨架程序 | | `-- pci-skeleton.c | `-- PCI驱动范例 | `-- i810_audio.c `-- 22 |-- 范例代码 | |-- oops范例 | | |-- oops_example.asm | | `-- oops_example.c | `-- proc范例 | `-- sim_proc.c `-- 内核调试工具 |-- ddd-3.3.11.tar.gz |-- gdbmod-2.4.bz2 |-- kdb-v4.4-2.6.15-rc5-common-1.bz2 |-- kdb-v4.4-2.6.15-rc5-common-2.bz2 |-- kdb-v4.4-2.6.15-rc5-i386-1.bz2 `-- linux-2.6.15.5-kgdb-2.4.tar.tar 73 directories, 91 files

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值