浅析linux2.6 framebuffer

本文解析了Linux内核中的Framebuffer驱动架构及其实现原理。介绍了fbmem.c作为核心组件的作用,并详细阐述了fb_info结构体的关键作用及其包含的重要字段。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Linux内核中,Framebuffer(帖缓冲)驱动是显示驱动的标准,Framebuffer

将显示设备抽象为帖缓冲区,用户通过内存映射到进程地址空间之后,就可以直

接进行读写操作,且写操作可以立即在屏幕上进行显示,在Linux内核

/linux/drivers/video/下有相关的显示驱动与接口,其中Frmaebuffer驱动接口为

fbmem.c

framebuffer驱动架构如下:

由此我们可以看出,fbmem.c是整个framebuffer的核心,它有应用程序调用的接口,又跟具体设备打交道。在具体驱动中,fd_info是核心结构,所有的操作都是围绕这个结构展开。

所以在解析源码之前,首先先介绍一些重要的结构。

fb.h中:

Fb_info是最关键的一个数据结构。它包含了关于帧缓冲设备的属性和操作的完整描述。

struct fb_info {

       int node;

       int flags;

       struct mutex lock;        /* Lock for open/release/ioctl funcs */

       struct mutex mm_lock;              /* Lock for fb_mmap and smem_* fields */

       struct fb_var_screeninfo var;    /* Current var可变参数*/

       struct fb_fix_screeninfo fix;       /* Current fix固定参数*/

       struct fb_monspecs monspecs;       /* Current Monitor specs显示器标准  */

       struct work_struct queue;  /* Framebuffer event queue帧缓冲事件队列*/

       struct fb_pixmap pixmap;   /* Image hardware mapper图像硬件mapper*/

       struct fb_pixmap sprite;      /* Cursor hardware mapper光标硬件mapper*/

       struct fb_cmap cmap;        /* Current cmap目前的颜色表*/

       struct list_head modelist;      /* mode list */

       struct fb_videomode *mode;     /* current mode目前的video模式*/

 

#ifdef CONFIG_FB_BACKLIGHT

       /* assigned backlight device */

       /* set before framebuffer registration,

          remove after unregister */

       struct backlight_device *bl_dev;/*对应的背光设置*/

 

       /* Backlight level curve */

       struct mutex bl_curve_mutex;  

       u8 bl_curve[FB_BACKLIGHT_LEVELS];/*背光调整*/

#endif

#ifdef CONFIG_FB_DEFERRED_IO

       struct delayed_work deferred_work;

       struct fb_deferred_io *fbdefio;

#endif

 

       struct fb_ops *fbops; /*帧缓冲操作函数集*/

       struct device *device;         /* This is the parent */

       struct device *dev;             /* This is this fb device */

       int class_flag;                    /* private sysfs flags */

#ifdef CONFIG_FB_TILEBLITTING

       struct fb_tile_ops *tileops;    /* Tile Blitting图块Blitting*/

#endif

       char __iomem *screen_base;   /* Virtual address虚拟基地址*/

       unsigned long screen_size;      /* Amount of ioremapped VRAM or 0虚拟内存大小 */

       void *pseudo_palette;        /* Fake palette of 16 colors16色颜色表*/

#define FBINFO_STATE_RUNNING0

#define FBINFO_STATE_SUSPENDED 1

       u32 state;                   /* Hardware state i.e suspend硬件状态 */

       void *fbcon_par;                /* fbcon use-only private area */

       /* From here on everything is device dependent */

       void *par;

       /* we need the PCI or similiar aperture base/size not

          smem_start/size as smem_start may just be an object

          allocated inside the aperture so may not actually overlap */

       resource_size_t aperture_base;

       resource_size_t aperture_size;

};

Fb_info中的fb_ops结构为指向底层操作函数的指针。这些函数是需要驱动工程师编写实现的。

struct fb_ops {

       /* open/release and usage marking */

       struct module *owner;

       int (*fb_open)(struct fb_info *info, int user);/*打开设备*/

       int (*fb_release)(struct fb_info *info, int user);/*释放设备*/

 

       /* For framebuffers with strange non linear layouts or that do not

        * work with normal memory mapped access

        */

       ssize_t (*fb_read)(struct fb_info *info, char __user *buf,

                        size_t count, loff_t *ppos);

       ssize_t (*fb_write)(struct fb_info *info, const char __user *buf,

                         size_t count, loff_t *ppos);

 

       /* checks var and eventually tweaks it to something supported,

        * DO NOT MODIFY PAR检查可变参数并调整到支持的值*/

       int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);

 

       /* set the video mode according to info->var根据info->var设置video模式*/

       int (*fb_set_par)(struct fb_info *info);

       /*设置color寄存器*/

       /* set color register */

       int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,

                         unsigned blue, unsigned transp, struct fb_info *info);

       /*批量设置color寄存器,设置颜色表*/

       /* set color registers in batch */

       int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);

       /*显示空白*/

       /* blank display */

       int (*fb_blank)(int blank, struct fb_info *info);

 

       /* pan display */

       int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);

 

       /* Draws a rectangle矩形填充*/

       void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);

       /* Copy data from area to another从其他地方复制数据*/

       void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);

       /* Draws a image to the display图形填充*/

       void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);

 

       /* Draws cursor */

       int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);

 

       /* Rotates the display */

       void (*fb_rotate)(struct fb_info *info, int angle);

 

       /* wait for blit idle, optional */

       int (*fb_sync)(struct fb_info *info);

 

       /* perform fb specific ioctl (optional) */

       int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,

                     unsigned long arg);

 

       /* Handle 32bit compat ioctl (optional) */

       int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,

                     unsigned long arg);

 

       /* perform fb specific mmap */

       int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);

 

       /* get capability given var */

       void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,

                         struct fb_var_screeninfo *var);

 

       /* teardown any resources to do with this framebuffer */

       void (*fb_destroy)(struct fb_info *info);

};

fb_info结构中的fb_var_screeninfo记录用户可修改的显示器控制参数,包括屏幕的分辨率和每个像素比特数。其中结构中xres定义一行有多少像素点,yres定义一列由多少像素点,bits_per_pixel定义每个点用了多少字节表示。

struct fb_var_screeninfo {

       __u32 xres;                 /* visible resolution     可见解析度    */

       __u32 yres;

       __u32 xres_virtual;            /* virtual resolution     虚拟解析度    */

       __u32 yres_virtual;

       __u32 xoffset;                    /* offset from virtual to visible */

       __u32 yoffset;                    /* resolution                */

 

       __u32 bits_per_pixel;        /* guess what      每像素比特数       */

       __u32 grayscale;        /* != 0 Graylevels instead of colors0时为灰度*/

 

       struct fb_bitfield red;          /* bitfield in fb mem if true color, */

       struct fb_bitfield green;      /* else only length is significant */

       struct fb_bitfield blue;

       struct fb_bitfield transp;     /* transparency   透明度           */    

 

       __u32 nonstd;                   /* != 0 Non standard pixel format非零表示像素格式*/

 

       __u32 activate;                  /* see FB_ACTIVATE_*             */

 

       __u32 height;                    /* height of picture in mm    */

       __u32 width;               /* width of picture in mm     */

 

       __u32 accel_flags;            /* (OBSOLETE) see fb_info.flags */

 

       /* Timing: All values in pixclocks, except pixclock (of course) */

       __u32 pixclock;                  /* pixel clock in ps (pico seconds)像素时钟(皮秒)*/

       __u32 left_margin;             /* time from sync to picture行切换从同步到绘图之间的延迟   */

       __u32 right_margin;          /* time from picture to sync行切换从绘图到同步之间的延迟   */

       __u32 upper_margin;        /* time from sync to picture帧切换从同步到绘图之间的延迟   */

       __u32 lower_margin;    /*帧切换从绘图到同步之间的延迟*/

       __u32 hsync_len;              /* length of horizontal sync水平同步的长度  */

       __u32 vsync_len;              /* length of vertical sync    垂直同步长度*/

       __u32 sync;                /* see FB_SYNC_*             */

       __u32 vmode;                    /* see FB_VMODE_*          */

       __u32 rotate;                     /* angle we rotate counter clockwise顺时针旋转的角度*/

       __u32 reserved[5];            /* Reserved for future compatibility保留*/

};

fb_info结构中的fd_fix_screeninfo成员记录了用户的不可修改的显示器参数,如屏幕尺寸,RGB,缓冲区物理地址,长度等,当对设备进行映射时,就从该结构中拿出物理地址。

struct fb_fix_screeninfo {

       char id[16];                 /* identification string eg "TT Builtin"字符串形式ide标示符*/

       unsigned long smem_start;       /* Start of frame buffer mem fb缓存的开始位置*/

                                  /* (physical address) */

       __u32 smem_len;                     /* Length of frame buffer mem缓存长度*/

       __u32 type;                /* see FB_TYPE_*             */

       __u32 type_aux;                /* Interleave for interleaved Planes */

       __u32 visual;                     /* see FB_VISUAL_*           */

       __u16 xpanstep;                /* zero if no hardware panning  */

       __u16 ypanstep;                /* zero if no hardware panning  */

       __u16 ywrapstep;              /* zero if no hardware ywrap    */

       __u32 line_length;             /* length of a line in bytes  1行的字节数 */

       unsigned long mmio_start; /* Start of Memory Mapped I/O 内存映射的I/O开始i位置 */

                                  /* (physical address) */

       __u32 mmio_len;               /* Length of Memory Mapped I/O 内存映射IO长度*/

       __u32 accel;               /* Indicate to driver which   */

                                  /*  specific chip/card we have  */

       __u16 reserved[3];            /* Reserved for future compatibility保留*/

};

fb_var_screeninfo结构中的fb_bitfield结构描述了每一像素显示缓冲区的组织方式。

struct fb_bitfield {

       __u32 offset;                     /* beginning of bitfield位域偏移*/

       __u32 length;                    /* length of bitfield             位域长度*/

       __u32 msb_right;        /* != 0 : Most significant bit is */

                                  /* right */

};

 

如果驱动支持framebuffer,那么首先执行fbmem.c中的subsys_initcall(fbmem_init)。因为这是初始化framebuffer子系统。

static int __init

fbmem_init(void)

{

       /*创建proc相关文件/proc/fb*/

       proc_create("fb", 0, NULL, &fb_proc_fops);

       /*注册cdev主设备号为29*/

       if (register_chrdev(FB_MAJOR,"fb",&fb_fops))

              printk("unable to get major %d for fb devs\n", FB_MAJOR);

       /*创建class类,在/sys/class*/

       fb_class = class_create(THIS_MODULE, "graphics");

       if (IS_ERR(fb_class)) {

              printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));

              fb_class = NULL;

       }

       return 0;

}

fbmem_init做了(1)创建proc文件(2)注册cdev主设备号为29(3)创建class

 

fb子系统初始化之后,下面就是具体驱动的实现了。我们以s3c2410fb.c为分析对象:

首先执行module_init(s3c2410fb_init)

int __init s3c2410fb_init(void)

{

       /*注册平台驱动*/

       int ret = platform_driver_register(&s3c2410fb_driver);

 

       if (ret == 0)

              ret = platform_driver_register(&s3c2412fb_driver);

 

       return ret;

}

s3c2410fb_init做了(1)注册平台驱动.

这些还要查一句:

之前我们已经提到:在mini2440_machine_init函数中,很多设备都是以platform_device的形式加载进内核。其中就包括LCD

s3c24xx_fb_set_platdata(&mini2440_fb_info);

void __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info *pd)

{

       struct s3c2410fb_mach_info *npd;

 

       npd = kmalloc(sizeof(*npd), GFP_KERNEL);

       if (npd) {

              memcpy(npd, pd, sizeof(*npd));

              s3c_device_lcd.dev.platform_data = npd;

       } else {

              printk(KERN_ERR "no memory for LCD platform data\n");

       }

}

这个函数的目的就是s3c_device_lcd.dev.platform_data=mini2440_fb_info

static struct s3c2410fb_mach_info mini2440_fb_info __initdata = {

       .displays       = &mini2440_lcd_cfg,

       .num_displays      = 1,

       .default_display = 0,

 

       .gpccon =       0xaa955699,

       .gpccon_mask =  0xffc003cc,

       .gpcup =        0x0000ffff,

       .gpcup_mask =   0xffffffff,

 

       .gpdcon =       0xaa95aaa1,

       .gpdcon_mask =  0xffc0fff0,

       .gpdup =        0x0000faff,

       .gpdup_mask =   0xffffffff,

 

 

       .lpcsel           = 0xf82,

};

static struct s3c2410fb_display mini2440_lcd_cfg __initdata = {

 

#if !defined (LCD_CON5)

       .lcdcon5 = S3C2410_LCDCON5_FRM565 |

                       S3C2410_LCDCON5_INVVLINE |

                       S3C2410_LCDCON5_INVVFRAME |

                       S3C2410_LCDCON5_PWREN |

                       S3C2410_LCDCON5_HWSWP,

#else

       .lcdcon5 = LCD_CON5,

#endif

 

       .type             = S3C2410_LCDCON1_TFT,

 

       .width            = LCD_WIDTH,

       .height          = LCD_HEIGHT,

 

       .pixclock = LCD_PIXCLOCK,

       .xres              = LCD_WIDTH,

       .yres             = LCD_HEIGHT,

       .bpp              = 16,

       .left_margin   = LCD_LEFT_MARGIN + 1,

       .right_margin       = LCD_RIGHT_MARGIN + 1,

       .hsync_len    = LCD_HSYNC_LEN + 1,

       .upper_margin     = LCD_UPPER_MARGIN + 1,

       .lower_margin      = LCD_LOWER_MARGIN + 1,

       .vsync_len    = LCD_VSYNC_LEN + 1,

};

mini2440_lcd_cfg mini2440_fb_info之后都将跟fb_info结构中的var fix结构产生联系。

LCDplatform_device结构:

struct platform_device s3c_device_lcd = {

       .name             = "s3c2410-lcd",

       .id            = -1,

       /***num_resources = 2***/

       .num_resources     = ARRAY_SIZE(s3c_lcd_resource),

       .resource        = s3c_lcd_resource,

       .dev              = {

              .dma_mask          = &s3c_device_lcd_dmamask,

              .coherent_dma_mask = 0xffffffffUL

       }

};

回到s3c2410fb_init,加载完platform_driver之后:

static struct platform_driver s3c2410fb_driver = {

       .probe           = s3c2410fb_probe,

       .remove        = s3c2410fb_remove,

       .suspend       = s3c2410fb_suspend,

       .resume        = s3c2410fb_resume,

       .driver           = {

              .name    = "s3c2410-lcd",

              .owner    = THIS_MODULE,

       },

};

就会执行s3c2410fb_probe函数。

static int __init s3c2410fb_probe(struct platform_device *pdev)

{

       return s3c24xxfb_probe(pdev, DRV_S3C2410);

}

static int __init s3c24xxfb_probe(struct platform_device *pdev,

                             enum s3c_drv_type drv_type)

{

       struct s3c2410fb_info *info;

       struct s3c2410fb_display *display;

       struct fb_info *fbinfo;

       struct s3c2410fb_mach_info *mach_info;

       struct resource *res;

       int ret;

       int irq;

       int i;

       int size;

       u32 lcdcon1;

       /*获取mini2440_fb_info结构*/

       mach_info = pdev->dev.platform_data;

       if (mach_info == NULL) {

              dev_err(&pdev->dev,

                     "no platform data for lcd, cannot attach\n");

              return -EINVAL;

       }

       /*mach_info->default_display0*/

       if (mach_info->default_display >= mach_info->num_displays) {

              dev_err(&pdev->dev, "default is %d but only %d displays\n",

                     mach_info->default_display, mach_info->num_displays);

              return -EINVAL;

       }

       /*display就是 mini2440_lcd_cfg结构*/

       display = mach_info->displays + mach_info->default_display;

    /***获取中断号***/

       irq = platform_get_irq(pdev, 0);

       if (irq < 0) {

              dev_err(&pdev->dev, "no irq for device\n");

              return -ENOENT;

       }

    /***分配fb_info结构,并将fbinfo->dev指向pdev->dev***/

       fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);

       if (!fbinfo)

              return -ENOMEM;

    /***pdev结构和fbinfo结构联系起来 pdev->dev->p->data = fbinfo***/

       platform_set_drvdata(pdev, fbinfo);

      

       info = fbinfo->par;

       info->dev = &pdev->dev;

       info->drv_type = drv_type;

    /***获取LCDCON寄存器对应的内存资源***/

       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

       if (res == NULL) {

              dev_err(&pdev->dev, "failed to get memory registers\n");

              ret = -ENXIO;

              goto dealloc_fb;

       }

 

       size = (res->end - res->start) + 1;

       /***申请内存区域***/

       info->mem = request_mem_region(res->start, size, pdev->name);

       if (info->mem == NULL) {

              dev_err(&pdev->dev, "failed to get memory region\n");

              ret = -ENOENT;

              goto dealloc_fb;

       }

    /***io重映射***/

       info->io = ioremap(res->start, size);

       if (info->io == NULL) {

              dev_err(&pdev->dev, "ioremap() of registers failed\n");

              ret = -ENXIO;

              goto release_mem;

       }

    /***将映射后LCDINTPND的地址info->irq_base***/

       info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);

 

       dprintk("devinit\n");

       /*填充fixid*/

       strcpy(fbinfo->fix.id, driver_name);

 

       /* Stop the video */

       /***LCDCON1寄存器***/

       lcdcon1 = readl(info->io + S3C2410_LCDCON1);

       /***允许视频输出和LCD信号***/

       writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);

    /***填写固定参数结构***/

       fbinfo->fix.type         = FB_TYPE_PACKED_PIXELS;

       fbinfo->fix.type_aux         = 0;

       fbinfo->fix.xpanstep         = 0;

       fbinfo->fix.ypanstep        = 0;

       fbinfo->fix.ywrapstep       = 0;

       fbinfo->fix.accel        = FB_ACCEL_NONE;

       /*填充可变参数*/

       fbinfo->var.nonstd           = 0;

       fbinfo->var.activate         = FB_ACTIVATE_NOW;

       fbinfo->var.accel_flags     = 0;

       fbinfo->var.vmode           = FB_VMODE_NONINTERLACED;

       /*填充操作函数集*/

       fbinfo->fbops                  = &s3c2410fb_ops;

       fbinfo->flags             = FBINFO_FLAG_DEFAULT;

       fbinfo->pseudo_palette      = &info->pseudo_pal;

 

       for (i = 0; i < 256; i++)

              info->palette_buffer[i] = PALETTE_BUFF_CLEAR;

       /***申请中断***/

       ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);

       if (ret) {

              dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);

              ret = -EBUSY;

              goto release_regs;

       }

    /***获取LCD时钟***/

       info->clk = clk_get(NULL, "lcd");

       if (!info->clk || IS_ERR(info->clk)) {

              printk(KERN_ERR "failed to get lcd clock source\n");

              ret = -ENOENT;

              goto release_irq;

       }

       /***开启LCD时钟***/

       clk_enable(info->clk);

       dprintk("got and enabled clock\n");

 

       msleep(1);

 

       info->clk_rate = clk_get_rate(info->clk);

 

       /* find maximum required memory size for display */

       /*计算一帧数据所占字节数*/

       for (i = 0; i < mach_info->num_displays; i++) {

              unsigned long smem_len = mach_info->displays[i].xres;/*320*/

 

              smem_len *= mach_info->displays[i].yres; /*240*/

              smem_len *= mach_info->displays[i].bpp;/*16*/

              smem_len >>= 3;

              if (fbinfo->fix.smem_len < smem_len)

                     /*将一帧数据大小赋值给smem_len*/

                     fbinfo->fix.smem_len = smem_len;

       }

 

       /* Initialize video memory */

       /*初始化LCD缓存*/

       ret = s3c2410fb_map_video_memory(fbinfo);

       if (ret) {

              printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);

              ret = -ENOMEM;

              goto release_clock;

       }

 

       dprintk("got video memory\n");

       /* mini2440_lcd_cfgxres yres bpp赋值给var*/

       fbinfo->var.xres = display->xres;

       fbinfo->var.yres = display->yres;

       fbinfo->var.bits_per_pixel = display->bpp;

       /*初始化相应寄存器*/

       s3c2410fb_init_registers(fbinfo);

       /*填充var可变参数*/

       s3c2410fb_check_var(&fbinfo->var, fbinfo);

       /*注册时钟频率*/

       ret = s3c2410fb_cpufreq_register(info);

       if (ret < 0) {

              dev_err(&pdev->dev, "Failed to register cpufreq\n");

              goto free_video_memory;

       }

       /*注册帧缓冲设备*/

       ret = register_framebuffer(fbinfo);

       if (ret < 0) {

              printk(KERN_ERR "Failed to register framebuffer device: %d\n",

                     ret);

              goto free_cpufreq;

       }

 

       /* create device files */

       /*创建/sys设备文件*/

       ret = device_create_file(&pdev->dev, &dev_attr_debug);

       if (ret) {

              printk(KERN_ERR "failed to add debug attribute\n");

       }

 

       printk(KERN_INFO "fb%d: %s frame buffer device\n",

              fbinfo->node, fbinfo->fix.id);

 

       return 0;

 

 free_cpufreq:

       s3c2410fb_cpufreq_deregister(info);

free_video_memory:

       s3c2410fb_unmap_video_memory(fbinfo);

release_clock:

       clk_disable(info->clk);

       clk_put(info->clk);

release_irq:

       free_irq(irq, info);

release_regs:

       iounmap(info->io);

release_mem:

       release_resource(info->mem);

       kfree(info->mem);

dealloc_fb:

       platform_set_drvdata(pdev, NULL);

       framebuffer_release(fbinfo);

       return ret;

}

由上可知:probe函数主要做了以下事情:

(1)      分配一个fb_info结构

(2)      获取平台资源(中断和IO基地址)

(3)      进行io重映射和中断申请,并将重映射地址和中断基地址给info相应结构

(4)      初始化LCD缓存

(5)      填充fb_info结构中的fix域和var域及fbops等:通过mini2440_lcd_cfg结构填充var

(6)      开启LCD时钟

(7)      初始化相应LCD寄存器

(8)      注册帧缓冲

(9)      创建/sys设备文件

对于(4)对应的函数:

static int __init s3c2410fb_map_video_memory(struct fb_info *info)

{

       struct s3c2410fb_info *fbi = info->par;

       dma_addr_t map_dma;

       unsigned map_size = PAGE_ALIGN(info->fix.smem_len);

 

       dprintk("map_video_memory(fbi=%p) map_size %u\n", fbi, map_size);

       /*分配内存(虚拟地址)*/

       info->screen_base = dma_alloc_writecombine(fbi->dev, map_size,

                                            &map_dma, GFP_KERNEL);

 

       if (info->screen_base) {

              /* prevent initial garbage on screen */

              /***对内存清空***/

              dprintk("map_video_memory: clear %p:%08x\n",

                     info->screen_base, map_size);

              memset(info->screen_base, 0x00, map_size);

        /***此时的map_dma是页表首地址(物理地址)***/

              info->fix.smem_start = map_dma;

 

              dprintk("map_video_memory: dma=%08lx cpu=%p size=%08x\n",

                     info->fix.smem_start, info->screen_base, map_size);

       }

 

       return info->screen_base ? 0 : -ENOMEM;

}

对于(8)注册帧缓冲:

int

register_framebuffer(struct fb_info *fb_info)

{

       int i;

       struct fb_event event;

       struct fb_videomode mode;

 

       if (num_registered_fb == FB_MAX)

              return -ENXIO;

 

       if (fb_check_foreignness(fb_info))

              return -ENOSYS;

 

       /* check all firmware fbs and kick off if the base addr overlaps */

       /*检测所有已经注册的fb结构是否跟要注册的fd结构缓冲地址重叠*/

       for (i = 0 ; i < FB_MAX; i++) {

              if (!registered_fb[i])

                     continue;

 

              if (registered_fb[i]->flags & FBINFO_MISC_FIRMWARE) {

                     if (fb_do_apertures_overlap(registered_fb[i], fb_info)) {

                           printk(KERN_ERR "fb: conflicting fb hw usage "

                                  "%s vs %s - removing generic driver\n",

                                  fb_info->fix.id,

                                  registered_fb[i]->fix.id);

                           unregister_framebuffer(registered_fb[i]);

                           break;

                     }

              }

       }

       /*注册计数加一*/

       num_registered_fb++;

       for (i = 0 ; i < FB_MAX; i++)

              /*找到数组中第一个没有被注册的元素*/

              if (!registered_fb[i])

                     break;

       /*将所在数组的位置信息给fb_info方便以后查找*/

       fb_info->node = i;

       mutex_init(&fb_info->lock);

       mutex_init(&fb_info->mm_lock);

       /*创建设备文件*/

       fb_info->dev = device_create(fb_class, fb_info->device,

                                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 = 1;

                     fb_info->pixmap.scan_align = 1;

                     fb_info->pixmap.access_align = 32;

                     fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;

              }

       }     

       fb_info->pixmap.offset = 0;

 

       if (!fb_info->pixmap.blit_x)

              fb_info->pixmap.blit_x = ~(u32)0;

 

       if (!fb_info->pixmap.blit_y)

              fb_info->pixmap.blit_y = ~(u32)0;

 

       if (!fb_info->modelist.prev || !fb_info->modelist.next)

              INIT_LIST_HEAD(&fb_info->modelist);

       /*填充mode结构*/

       fb_var_to_videomode(&mode, &fb_info->var);

       fb_add_videomode(&mode, &fb_info->modelist);

       /*fb_info结构赋予数组*/

       registered_fb[i] = fb_info;

 

       event.info = fb_info;

       if (!lock_fb_info(fb_info))

              return -ENODEV;

       fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);

       unlock_fb_info(fb_info);

       return 0;

}

我们从上可知:register_framebuffer函数有这样一个技巧:有一个registered_fb全局数组,这个数组是存放所以已注册的fb结构,当我们应用程序open ioctl等通过次设备号就能找到fb结构

register_framebuffer主要做了以下事情:

(1)      检测registered_fb数组中已经注册的fb,判断他们的缓冲地址是否跟要注册的地址冲突,如果冲突,则将已经注册的unregister

(2)      找到registered_fb中第一个没有被注册的位置,并将位置信息给fb结构(其实就是次设备号)

(3)      创建设备文件

(4)      fb_info记录到registered_fb数组。

 

就此,fb基本的驱动就实现了。

当应用程序使用open系统调用打开/dev/fb0时,经过一系列的过程到了fbmem.cfb_open函数。

static int

fb_open(struct inode *inode, struct file *file)

__acquires(&info->lock)

__releases(&info->lock)

{

       /*获取次设备号*/

       int fbidx = iminor(inode);

       struct fb_info *info;

       int res = 0;

       /*通过次设备号获得fd_info结构*/

       if (fbidx >= FB_MAX)

              return -ENODEV;

       /*通过次设备号在全局数据中找到对应的fb_info结构*/

       info = registered_fb[fbidx];

       if (!info)

              request_module("fb%d", fbidx);

       info = registered_fb[fbidx];

       if (!info)

              return -ENODEV;

       mutex_lock(&info->lock);

       if (!try_module_get(info->fbops->owner)) {

              res = -ENODEV;

              goto out;

       }

       /*info结构赋给file私有指针域*/

       file->private_data = info;

       /*如果ops域中有open函数则执行open函数*/

       if (info->fbops->fb_open) {

              res = info->fbops->fb_open(info,1);

              if (res)

                     module_put(info->fbops->owner);

       }

#ifdef CONFIG_FB_DEFERRED_IO

       if (info->fbdefio)

              fb_deferred_io_open(info, inode, file);

#endif

out:

       mutex_unlock(&info->lock);

       return res;

}

从上可以看出:针对s3c2410fb来说,open函数的作用仅仅是将2410对应的fb_info结构赋值于file的私有指针域。

对于fb来说,read write系统调用没有意义,下面介绍Ioctl.

static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)

{

       struct inode *inode = file->f_path.dentry->d_inode;

       int fbidx = iminor(inode);

       /*通过次设备号获得fb_info结构*/

       struct fb_info *info = registered_fb[fbidx];

 

       return do_fb_ioctl(info, cmd, arg);

}

static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,

                     unsigned long arg)

{

       struct fb_ops *fb;

       struct fb_var_screeninfo var;

       struct fb_fix_screeninfo fix;

       struct fb_con2fbmap con2fb;

       struct fb_cmap cmap_from;

       struct fb_cmap_user cmap;

       struct fb_event event;

       void __user *argp = (void __user *)arg;

       long ret = 0;

 

       switch (cmd) {

              /*获得可变参数信息*/

       case FBIOGET_VSCREENINFO:

              if (!lock_fb_info(info))

                     return -ENODEV;

              var = info->var;

              unlock_fb_info(info);

 

              ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0;

              break;

       case FBIOPUT_VSCREENINFO:

              /*修改可变参数信息*/

              if (copy_from_user(&var, argp, sizeof(var)))

                     return -EFAULT;

              if (!lock_fb_info(info))

                     return -ENODEV;

              acquire_console_sem();

              info->flags |= FBINFO_MISC_USEREVENT;

              ret = fb_set_var(info, &var);

              info->flags &= ~FBINFO_MISC_USEREVENT;

              release_console_sem();

              unlock_fb_info(info);

              if (!ret && copy_to_user(argp, &var, sizeof(var)))

                     ret = -EFAULT;

              break;

       case FBIOGET_FSCREENINFO:

              /*获取固定参数*/

              if (!lock_fb_info(info))

                     return -ENODEV;

              fix = info->fix;

              unlock_fb_info(info);

 

              ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0;

              break;

       case FBIOPUTCMAP:

              if (copy_from_user(&cmap, argp, sizeof(cmap)))

                     return -EFAULT;

              ret = fb_set_user_cmap(&cmap, info);

              break;

       case FBIOGETCMAP:

              if (copy_from_user(&cmap, argp, sizeof(cmap)))

                     return -EFAULT;

              if (!lock_fb_info(info))

                     return -ENODEV;

              cmap_from = info->cmap;

              unlock_fb_info(info);

              ret = fb_cmap_to_user(&cmap_from, &cmap);

              break;

       case FBIOPAN_DISPLAY:

              /*滚屏*/

              if (copy_from_user(&var, argp, sizeof(var)))

                     return -EFAULT;

              if (!lock_fb_info(info))

                     return -ENODEV;

              acquire_console_sem();

              ret = fb_pan_display(info, &var);

              release_console_sem();

              unlock_fb_info(info);

              if (ret == 0 && copy_to_user(argp, &var, sizeof(var)))

                     return -EFAULT;

              break;

       case FBIO_CURSOR:

              ret = -EINVAL;

              break;

       case FBIOGET_CON2FBMAP:

              if (copy_from_user(&con2fb, argp, sizeof(con2fb)))

                     return -EFAULT;

              if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)

                     return -EINVAL;

              con2fb.framebuffer = -1;

              event.data = &con2fb;

              if (!lock_fb_info(info))

                     return -ENODEV;

              event.info = info;

              fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP, &event);

              unlock_fb_info(info);

              ret = copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;

              break;

       case FBIOPUT_CON2FBMAP:

              if (copy_from_user(&con2fb, argp, sizeof(con2fb)))

                     return -EFAULT;

              if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)

                     return -EINVAL;

              if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX)

                     return -EINVAL;

              if (!registered_fb[con2fb.framebuffer])

                     request_module("fb%d", con2fb.framebuffer);

              if (!registered_fb[con2fb.framebuffer]) {

                     ret = -EINVAL;

                     break;

              }

              event.data = &con2fb;

              if (!lock_fb_info(info))

                     return -ENODEV;

              event.info = info;

              ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event);

              unlock_fb_info(info);

              break;

       case FBIOBLANK:

              if (!lock_fb_info(info))

                     return -ENODEV;

              acquire_console_sem();

              info->flags |= FBINFO_MISC_USEREVENT;

              ret = fb_blank(info, arg);

              info->flags &= ~FBINFO_MISC_USEREVENT;

              release_console_sem();

              unlock_fb_info(info);

              break;

       default:

              if (!lock_fb_info(info))

                     return -ENODEV;

              fb = info->fbops;

              if (fb->fb_ioctl)

                     ret = fb->fb_ioctl(info, cmd, arg);

              else

                     ret = -ENOTTY;

              unlock_fb_info(info);

       }

       return ret;

}

获取fix var参数很简单,就是copy_to_user就可以,下面浅析修改var参数。

case FBIOPUT_VSCREENINFO:

              /*修改可变参数信息*/

              if (copy_from_user(&var, argp, sizeof(var)))

                     return -EFAULT;

              if (!lock_fb_info(info))

                     return -ENODEV;

              acquire_console_sem();

              info->flags |= FBINFO_MISC_USEREVENT;

              ret = fb_set_var(info, &var);

              info->flags &= ~FBINFO_MISC_USEREVENT;

              release_console_sem();

              unlock_fb_info(info);

              if (!ret && copy_to_user(argp, &var, sizeof(var)))

                     ret = -EFAULT;

              break;

首先调用fb_set_var

int

fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)

{

       int flags = info->flags;

       int ret = 0;

       /*如果var参数存在违法*/

       if (var->activate & FB_ACTIVATE_INV_MODE) {

              struct fb_videomode mode1, mode2;

 

              fb_var_to_videomode(&mode1, var);

              fb_var_to_videomode(&mode2, &info->var);

              /* make sure we don't delete the videomode of current var */

              ret = fb_mode_is_equal(&mode1, &mode2);

 

              if (!ret) {

                  struct fb_event event;

 

                  event.info = info;

                  event.data = &mode1;

                  ret = fb_notifier_call_chain(FB_EVENT_MODE_DELETE, &event);

              }

 

              if (!ret)

                  fb_delete_videomode(&mode1, &info->modelist);

 

 

              ret = (ret) ? -EINVAL : 0;

              goto done;

       }

       /*使用memcmp将要修改的var参数和info本色的参数对比,如果完全一样则 activate = var->activate*/

       if ((var->activate & FB_ACTIVATE_FORCE) ||

           memcmp(&info->var, var, sizeof(struct fb_var_screeninfo))) {

              u32 activate = var->activate;

 

              if (!info->fbops->fb_check_var) {

                     *var = info->var;

                     goto done;

              }

              /*调用fb_check_var*/

              ret = info->fbops->fb_check_var(var, info);

 

              if (ret)

                     goto done;

 

              if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {

                     struct fb_var_screeninfo old_var;

                     struct fb_videomode mode;

 

                     if (info->fbops->fb_get_caps) {

                           ret = fb_check_caps(info, var, activate);

 

                           if (ret)

                                  goto done;

                     }

 

                     old_var = info->var;

                     info->var = *var;

                     /*调用set_var*/

                     if (info->fbops->fb_set_par) {

                           ret = info->fbops->fb_set_par(info);

 

                           if (ret) {

                                  info->var = old_var;

                                  printk(KERN_WARNING "detected "

                                         "fb_set_par error, "

                                         "error code: %d\n", ret);

                                  goto done;

                           }

                     }

 

                     fb_pan_display(info, &info->var);

                     fb_set_cmap(&info->cmap, info);

                     fb_var_to_videomode(&mode, &info->var);

 

                     if (info->modelist.prev && info->modelist.next &&

                         !list_empty(&info->modelist))

                           ret = fb_add_videomode(&mode, &info->modelist);

 

                     if (!ret && (flags & FBINFO_MISC_USEREVENT)) {

                           struct fb_event event;

                           int evnt = (activate & FB_ACTIVATE_ALL) ?

                                  FB_EVENT_MODE_CHANGE_ALL :

                                  FB_EVENT_MODE_CHANGE;

 

                           info->flags &= ~FBINFO_MISC_USEREVENT;

                           event.info = info;

                           event.data = &mode;

                           fb_notifier_call_chain(evnt, &event);

                     }

              }

       }

 

 done:

       return ret;

}

函数主要做了(1)函数调用fb_infofb_check_var

对应s3c2410就是

static int s3c2410fb_check_var(struct fb_var_screeninfo *var,

                            struct fb_info *info)

{

       struct s3c2410fb_info *fbi = info->par;

       struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;

       struct s3c2410fb_display *display = NULL;

       struct s3c2410fb_display *default_display = mach_info->displays +

                                             mach_info->default_display;

       int type = default_display->type;

       unsigned i;

 

       dprintk("check_var(var=%p, info=%p)\n", var, info);

 

       /* validate x/y resolution */

       /* choose default mode if possible */

       if (var->yres == default_display->yres &&

           var->xres == default_display->xres &&

           var->bits_per_pixel == default_display->bpp)

              display = default_display;

       else

              for (i = 0; i < mach_info->num_displays; i++)

                     if (type == mach_info->displays[i].type &&

                         var->yres == mach_info->displays[i].yres &&

                         var->xres == mach_info->displays[i].xres &&

                         var->bits_per_pixel == mach_info->displays[i].bpp) {

                           display = mach_info->displays + i;

                           break;

                     }

 

       if (!display) {

              dprintk("wrong resolution or depth %dx%d at %d bpp\n",

                     var->xres, var->yres, var->bits_per_pixel);

              return -EINVAL;

       }

 

       /* it is always the size as the display */

       var->xres_virtual = display->xres;

       var->yres_virtual = display->yres;

       var->height = display->height;

       var->width = display->width;

 

       /* copy lcd settings */

       var->pixclock = display->pixclock;

       var->left_margin = display->left_margin;

       var->right_margin = display->right_margin;

       var->upper_margin = display->upper_margin;

       var->lower_margin = display->lower_margin;

       var->vsync_len = display->vsync_len;

       var->hsync_len = display->hsync_len;

 

       fbi->regs.lcdcon5 = display->lcdcon5;

       /* set display type */

       fbi->regs.lcdcon1 = display->type;

 

       var->transp.offset = 0;

       var->transp.length = 0;

       /* set r/g/b positions */

       switch (var->bits_per_pixel) {

       case 1:

       case 2:

       case 4:

              var->red.offset     = 0;

              var->red.length    = var->bits_per_pixel;

              var->green    = var->red;

              var->blue      = var->red;

              break;

       case 8:

              if (display->type != S3C2410_LCDCON1_TFT) {

                     /* 8 bpp 332 */

                     var->red.length           = 3;

                     var->red.offset            = 5;

                     var->green.length       = 3;

                     var->green.offset = 2;

                     var->blue.length  = 2;

                     var->blue.offset   = 0;

              } else {

                     var->red.offset            = 0;

                     var->red.length           = 8;

                     var->green           = var->red;

                     var->blue             = var->red;

              }

              break;

       case 12:

              /* 12 bpp 444 */

              var->red.length           = 4;

              var->red.offset            = 8;

              var->green.length       = 4;

              var->green.offset = 4;

              var->blue.length  = 4;

              var->blue.offset   = 0;

              break;

 

       default:

       case 16:

              if (display->lcdcon5 & S3C2410_LCDCON5_FRM565) {

                     /* 16 bpp, 565 format */

                     var->red.offset            = 11;

                     var->green.offset = 5;

                     var->blue.offset   = 0;

                     var->red.length           = 5;

                     var->green.length       = 6;

                     var->blue.length  = 5;

              } else {

                     /* 16 bpp, 5551 format */

                     var->red.offset            = 11;

                     var->green.offset = 6;

                     var->blue.offset   = 1;

                     var->red.length           = 5;

                     var->green.length       = 5;

                     var->blue.length  = 5;

              }

              break;

       case 32:

              /* 24 bpp 888 and 8 dummy */

              var->red.length           = 8;

              var->red.offset            = 16;

              var->green.length       = 8;

              var->green.offset = 8;

              var->blue.length  = 8;

              var->blue.offset   = 0;

              break;

       }

       return 0;

}

函数主要就是填充一些var参数。

2)函数调用fb_infofb_set_var,对应s3c2410就是s3c2410fb_set_par

static int s3c2410fb_set_par(struct fb_info *info)

{

       struct fb_var_screeninfo *var = &info->var;

 

       switch (var->bits_per_pixel) {

       case 32:

       case 16:

       case 12:

              info->fix.visual = FB_VISUAL_TRUECOLOR;

              break;

       case 1:

              info->fix.visual = FB_VISUAL_MONO01;

              break;

       default:

              info->fix.visual = FB_VISUAL_PSEUDOCOLOR;

              break;

       }

 

       info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;

 

       /* activate this new configuration */

       /*根据var的值对LCD寄存器重新配置*/

       s3c2410fb_activate_var(info);

       return 0;

}

这个函数大体作用就是填充一些固定参数,根据varfix去重新配置LCD寄存器,这里不去详解,可根据2410的数据手册分析,代码容易理解。

应用程序要使用fb,那么就要通过mmap系统调用将fb的缓存地址映射到用户空间,这样用户空间对这段地址操作就完成了对LCD的操作,下面介绍fbmem.c fb_mmap函数。

static int fb_mmap(struct file *file, struct vm_area_struct * vma)

{

1     int fbidx = iminor(file->f_path.dentry->d_inode);

2     struct fb_info *info = registered_fb[fbidx];

3     struct fb_ops *fb = info->fbops;

4     unsigned long off;

5     unsigned long start;

6     u32 len;

       /*当第一页的地址大于0xfffff是非法的*/

7     if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))

8            return -EINVAL;

       /*off表示设备首位置所在的页面左移12*/

9     off = vma->vm_pgoff << PAGE_SHIFT;

10   if (!fb)

11          return -ENODEV;

12   mutex_lock(&info->mm_lock);

       /*如果存在fb-mmap*/

13   if (fb->fb_mmap) {

14          int res;

15          res = fb->fb_mmap(info, vma);

16          mutex_unlock(&info->mm_lock);

17          return res;

18   }

      

       /* frame buffer memory获得framebuffer的物理地址*/

19   start = info->fix.smem_start;

       /*framebuffer对应的内存末地址进行页对齐*/

20   len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);

21   if (off >= len) {

              /* memory mapped io */

22          off -= len;

23          if (info->var.accel_flags) {

24                 mutex_unlock(&info->mm_lock);

25                 return -EINVAL;

26          }

27          start = info->fix.mmio_start;

28          len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);

29   }

30   mutex_unlock(&info->mm_lock);

31   /*start=start&0xffff000的意思是start此时是原来start所在的页表*/

32   start &= PAGE_MASK;

       /*如果设备所映射的末页面大于framebuffer对应的内存末页面是违法的*/

33   if ((vma->vm_end - vma->vm_start + off) > len)

34          return -EINVAL;

35   off += start;

       /*这时vma->vm_pgoff就是被映射设备首地址*/

36   vma->vm_pgoff = off >> PAGE_SHIFT;

       /* This is an IO map - tell maydump to skip this VMA */

37   vma->vm_flags |= VM_IO | VM_RESERVED;

38   fb_pgprotect(file, vma, off);

       /*建立新页表*/

39   if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,

                          vma->vm_end - vma->vm_start, vma->vm_page_prot))

40          return -EAGAIN;

       return 0;

}

这个函数我的理解方式是这样的首先off = vma->vm_pgoff << PAGE_SHIFT;这一句的意思是之前所映射设备(不是该fb设备)首位置所在的页面左移12位。也就是之前设备对应的物理内存页帧号。off += start就是现在要映射的设备的物理内存。vma->vm_pgoff = off >> PAGE_SHIFT就是现在要映射的设备对应的物理内存页帧号。

最后,对应ioctlcmd的其他参数,目前还正在研究。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值