欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 养生 > Linux驱动编程 - Framebuffer子系统

Linux驱动编程 - Framebuffer子系统

2025/3/17 6:55:02 来源:https://blog.csdn.net/hinewcc/article/details/146143160  浏览:    关键词:Linux驱动编程 - Framebuffer子系统

目录

简介:

一、fbmem.c分析

二、mxsfb.c 分析

三、总结

1、fbmem.c 文件:搭建了LCD驱动框架

2、mxsfb.c 文件:底层硬件层

四、应用层如何使用 /dev/fb0


简介:


        Framebuffer(帧缓冲)是Linux系统中为显示设备提供的一套应用程序接口,它将显存抽象为一种设备,允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。 

  • 核心层(fbmem.c): 主要实现字符设备的创建,为不同的显示设备提供文件通用处理接口;同时创建graphics设备类,占据主设备号29。内核自带,无需自己开发

  • 硬件设备层(mxsfb.c): 主要提供显示设备的时序、显存、像素格式等硬件信息,实现显示设备的私有文件接口,并创建显示设备文件/dev/fbx(x=0~n)暴露给用户空间。 硬件设备层的代码需要驱动开发人员根据具体的显示设备完成开发。

一、fbmem.c分析


        FrameBuffer是抽象出来的通用的LCD驱动框架程序,它依赖底层LCD驱动调用 register_framebuffer() 注册 "fb_info" 结构体,之后会生成 /dev/fb0 字符设备,/dev/fb0 的file_operations 操作集就定义在 drivers/video/fbdev/core/fbmem.c 文件中。fbmem.c 为所有支持FrameBuffer的设备驱动提供通用的接口,避免重复工作。

        fbmem.c 处于Framebuffer设备驱动核心层,它为上层应用程序提供系统调用,也为下一层的特定硬件驱动提供接口。

fbmem.c核心代码如下:

/* 路径:drivers/video/fbdev/core/fbmem.c */
/* procfs操作集 */
static const struct file_operations fb_proc_fops = {.owner		= THIS_MODULE,.open		= proc_fb_open,.read		= seq_read,.llseek		= seq_lseek,.release	= seq_release,
};/* file_operations 操作集 */
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,
};static int __init
fbmem_init(void)
{/* 创建 /proc/fb 文件,并设置文件操作集 */proc_create("fb", 0, NULL, &fb_proc_fops);/* 注册字符设备,fb_fops提供系统调用 write、read、ioctl 等 */if (register_chrdev(FB_MAJOR,"fb",&fb_fops))printk("unable to get major %d for fb devs\n", FB_MAJOR);/* 自动创建设备节点:创建类,那在哪里创建设备的? */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;
}subsys_initcall(fbmem_init);

fbmem_init() 中做了3件事:

1)创建 /proc/fb 文件操作接口;

2)register_chrdev 注册字符设备驱动生成,提供 fb_fops 操作集;

3)创建类 "graphics"(没有创建设备)

此时并未生成 /dev 下的设备节点,因为没有创建设备,那么在哪里创建设备呢?继续往下看。

二、mxsfb.c 分析


        下面分析 i.MX 6ULL 开发板的 LCD 驱动框架。

        mxsfb.c 是NXP官方提供的 i.MX 6ULL 芯片针对一款 LCD 的驱动程序,它定义在 drivers/video/fbdev/mxsfb.c。当 of_match_table 与 imx6ull.dtsi 设备树文件中的compatible 属性值 "fsl,imx28-lcdif" 匹配时,则调用 mxsfb_probe 函数,该函数初始化并配置LCDIF控制器。

lcdif: lcdif@021c8000 {compatible = "fsl,imx6ul-lcdif", "fsl,imx28-lcdif";reg = <0x021c8000 0x4000>;interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>;clocks = <&clks IMX6UL_CLK_LCDIF_PIX>,<&clks IMX6UL_CLK_LCDIF_APB>,<&clks IMX6UL_CLK_DUMMY>;clock-names = "pix", "axi", "disp_axi";status = "okay";
};&lcdif {pinctrl-names = "default";pinctrl-0 = <&pinctrl_lcdif_dat			/* 使用到的 IO */&pinctrl_lcdif_ctrl>;display = <&display0>;status = "okay";display0: display {						/* LCD 属性信息 */bits-per-pixel = <24>;				/* 一个像素占用 24bit */bus-width = <24>;					/* 总线宽度 */display-timings {native-mode = <&timing0>;		/* 时序信息 */timing0: timing0 {clock-frequency = <51200000>;	/* LCD 像素时钟,单位 Hz */hactive = <1024>;				/* LCD X 轴像素个数 */vactive = <600>;				/* LCD Y 轴像素个数 */hfront-porch = <160>;			/* LCD hfp 参数 */hback-porch = <140>;			/* LCD hbp 参数 */hsync-len = <20>;				/* LCD hspw 参数 */vback-porch = <20>;				/* LCD vbp 参数 */vfront-porch = <12>;			/* LCD vfp 参数 */vsync-len = <3>;				/* LCD vspw 参数 */hsync-active = <0>;				/* hsync 数据线极性 */vsync-active = <0>;				/* vsync 数据线极性 */de-active = <1>;				/* de 数据线极性 */pixelclk-active = <0>;			/* clk 数据线先极性 */};};};
};

mxsfb.c 驱动代码分析:

static const struct of_device_id mxsfb_dt_ids[] = {{ .compatible = "fsl,imx23-lcdif", .data = &mxsfb_devtype[0], },{ .compatible = "fsl,imx28-lcdif", .data = &mxsfb_devtype[1], },{ /* sentinel */ }
};static struct platform_driver mxsfb_driver = {.probe = mxsfb_probe,.remove = mxsfb_remove,.shutdown = mxsfb_shutdown,.id_table = mxsfb_devtype,.driver = {.name = DRIVER_NAME,.of_match_table = mxsfb_dt_ids,.pm = &mxsfb_pm_ops,},
};module_platform_driver(mxsfb_driver);

mxsfb_probe() 函数主要功能:

1)framebuffer_alloc()分配一个fb_info

2)设置fb_info,如:fb_info->fbops = &mxsfb_ops; 设置操作集

3)硬件相关寄存器配置

4)register_framebuffer()注册fb_info

static int mxsfb_probe(struct platform_device *pdev)
{const struct of_device_id *of_id =of_match_device(mxsfb_dt_ids, &pdev->dev);struct resource *res;struct mxsfb_info *host;struct fb_info *fb_info;struct pinctrl *pinctrl;int irq = platform_get_irq(pdev, 0);int gpio, ret;if (of_id)pdev->id_entry = of_id->data;gpio = of_get_named_gpio(pdev->dev.of_node, "enable-gpio", 0);if (gpio == -EPROBE_DEFER)return -EPROBE_DEFER;if (gpio_is_valid(gpio)) {ret = devm_gpio_request_one(&pdev->dev, gpio, GPIOF_OUT_INIT_LOW, "lcd_pwr_en");if (ret) {dev_err(&pdev->dev, "faild to request gpio %d, ret = %d\n", gpio, ret);return ret;}}res = platform_get_resource(pdev, IORESOURCE_MEM, 0);if (!res) {dev_err(&pdev->dev, "Cannot get memory IO resource\n");return -ENODEV;}host = devm_kzalloc(&pdev->dev, sizeof(struct mxsfb_info), GFP_KERNEL);if (!host) {dev_err(&pdev->dev, "Failed to allocate IO resource\n");return -ENOMEM;}fb_info = framebuffer_alloc(sizeof(struct fb_info), &pdev->dev);if (!fb_info) {dev_err(&pdev->dev, "Failed to allocate fbdev\n");devm_kfree(&pdev->dev, host);return -ENOMEM;}host->fb_info = fb_info;fb_info->par = host;ret = devm_request_irq(&pdev->dev, irq, mxsfb_irq_handler, 0,dev_name(&pdev->dev), host);if (ret) {dev_err(&pdev->dev, "request_irq (%d) failed with error %d\n",irq, ret);ret = -ENODEV;goto fb_release;}host->base = devm_ioremap_resource(&pdev->dev, res);if (IS_ERR(host->base)) {dev_err(&pdev->dev, "ioremap failed\n");ret = PTR_ERR(host->base);goto fb_release;}host->pdev = pdev;platform_set_drvdata(pdev, host);host->devdata = &mxsfb_devdata[pdev->id_entry->driver_data];host->clk_pix = devm_clk_get(&host->pdev->dev, "pix");if (IS_ERR(host->clk_pix)) {host->clk_pix = NULL;ret = PTR_ERR(host->clk_pix);goto fb_release;}host->clk_axi = devm_clk_get(&host->pdev->dev, "axi");if (IS_ERR(host->clk_axi)) {host->clk_axi = NULL;ret = PTR_ERR(host->clk_axi);goto fb_release;}host->clk_disp_axi = devm_clk_get(&host->pdev->dev, "disp_axi");if (IS_ERR(host->clk_disp_axi)) {host->clk_disp_axi = NULL;ret = PTR_ERR(host->clk_disp_axi);goto fb_release;}host->reg_lcd = devm_regulator_get(&pdev->dev, "lcd");if (IS_ERR(host->reg_lcd))host->reg_lcd = NULL;fb_info->pseudo_palette = devm_kzalloc(&pdev->dev, sizeof(u32) * 16,GFP_KERNEL);if (!fb_info->pseudo_palette) {ret = -ENOMEM;goto fb_release;}INIT_LIST_HEAD(&fb_info->modelist);pm_runtime_enable(&host->pdev->dev);/* fb_info->fbops = &mxsfb_ops; 设置fb_ops操作集 */ret = mxsfb_init_fbinfo(host);if (ret != 0)goto fb_pm_runtime_disable;mxsfb_dispdrv_init(pdev, fb_info);if (!host->dispdrv) {pinctrl = devm_pinctrl_get_select_default(&pdev->dev);if (IS_ERR(pinctrl)) {ret = PTR_ERR(pinctrl);goto fb_pm_runtime_disable;}}if (!host->enabled) {writel(0, host->base + LCDC_CTRL);mxsfb_set_par(fb_info);mxsfb_enable_controller(fb_info);pm_runtime_get_sync(&host->pdev->dev);}ret = register_framebuffer(fb_info);if (ret != 0) {dev_err(&pdev->dev, "Failed to register framebuffer\n");goto fb_destroy;}console_lock();ret = fb_blank(fb_info, FB_BLANK_UNBLANK);console_unlock();if (ret < 0) {dev_err(&pdev->dev, "Failed to unblank framebuffer\n");goto fb_unregister;}dev_info(&pdev->dev, "initialized\n");return 0;fb_unregister:unregister_framebuffer(fb_info);
fb_destroy:if (host->enabled)clk_disable_unprepare(host->clk_pix);fb_destroy_modelist(&fb_info->modelist);
fb_pm_runtime_disable:pm_runtime_disable(&host->pdev->dev);devm_kfree(&pdev->dev, fb_info->pseudo_palette);
fb_release:framebuffer_release(fb_info);devm_kfree(&pdev->dev, host);return ret;
}

先看下 struct fb_info,这个结构体非常重要

struct fb_info {atomic_t count;         //打开计数器int node;               //文件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 */struct fb_pixmap sprite;	/* Cursor hardware mapper */struct fb_cmap cmap;		/* Current cmap */struct list_head modelist;      /* mode list */struct fb_videomode *mode;	/* current mode */#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_IOstruct delayed_work deferred_work;struct fb_deferred_io *fbdefio;
#endifstruct 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_TILEBLITTINGstruct fb_tile_ops *tileops;    /* Tile Blitting */
#endifunion {char __iomem *screen_base;	/* Virtual address 显存的起始地址,虚拟地址*/char *screen_buffer;};unsigned long screen_size;	/* Amount of ioremapped VRAM or 0 显存大小,即虚拟内存大小*/ void *pseudo_palette;		/* Fake palette of 16 colors */ 
#define FBINFO_STATE_RUNNING	0
#define FBINFO_STATE_SUSPENDED	1u32 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 similar aperture base/size notsmem_start/size as smem_start may just be an objectallocated inside the aperture so may not actually overlap */struct apertures_struct {unsigned int count;struct aperture {resource_size_t base;resource_size_t size;} ranges[0];} *apertures;bool skip_vt_switch; /* no VT switch on suspend/resume required */
};

register_framebuffer()注册fb_info功能:

1)创建设备节点

2)registered_fb[i] 添加fb_info

register_framebuffer()函数,路径:drivers\video\fbdev\core\fbmem.c

int
register_framebuffer(struct fb_info *fb_info)
{int ret;mutex_lock(&registration_lock);ret = do_register_framebuffer(fb_info);mutex_unlock(&registration_lock);return ret;
}

do_register_framebuffer()函数,路径:drivers\video\fbdev\core\fbmem.c

static int do_register_framebuffer(struct fb_info *fb_info)
{/* ... */for (i = 0 ; i < FB_MAX; i++)if (!registered_fb[i])break;fb_info->node = i;/* ... *//* 创建设备(在fbmem_init()中创建类)此时生成 /dev/fb0 节点 */fb_info->dev = device_create(fb_class, fb_info->device,MKDEV(FB_MAJOR, i), NULL, "fb%d", i);/* ... */registered_fb[i] = fb_info;//registered_fb[i]添加fb_info
}

主要做了2件事:

1)用 fbmem_init()中创建的类创建设备,会生成 /dev/fb0 节点

2)fb_info 添加到 registered_fb[i] 中,数组索引为次设备号

registered_fb[i] 又有什么用呢?我们先来分析下应用程序 open("/dev/fb0", ...) 过程

open("/dev/fb0", ...) 主设备号: 29, 次设备号: 0,最终调用fbmem.c 中的file_operations(fb_fops) 的.open(fb_open)。

static struct fb_info *get_fb_info(unsigned int idx)
{... ...fb_info = registered_fb[idx];    //registered_fb中通过index找到fb_info... ...return fb_info;
}static int
fb_open(struct inode *inode, struct file *file)
__acquires(&info->lock)
__releases(&info->lock)
{int fbidx = iminor(inode);    // 获取次设备号struct fb_info *info;... ... info = get_fb_info(fbidx);    // info = registered_fb[fbidx];... ...if (info->fbops->fb_open) {res = info->fbops->fb_open(info,1);    // 调用 fb_info 的 fbops->fb_open}
}

fb_open 中以次设备号为索引找到 registered_fb[i] 的 fb_info(fb_info *info = registered_fb[次设备号])。最终调用 fb_info 的 fbops->fb_open,也就是 mxsfb_probe 中注册的 fb_info。write、read、ioctl 等系统调用过程都类似。

同理,应用程序调用 ioctl(g_tLcd.fd, FBIOGET_VSCREENINFO, &fb_var),会先以次设备号为索引找到 registered_fb[i] 的 fb_info,然后获取 fb_info 中的参数传给应用。

static struct fb_info *file_fb_info(struct file *file)
{struct inode *inode = file_inode(file);int fbidx = iminor(inode);                    //获取次设备号struct fb_info *info = registered_fb[fbidx];  //获取 registered_fb[i] 中的 fb_inforeturn info;
}static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{struct fb_info *info = file_fb_info(file);... ...return do_fb_ioctl(info, cmd, arg);    // 处理数据
}static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,unsigned long arg)
{switch (cmd) {case FBIOGET_VSCREENINFO:if (!lock_fb_info(info))return -ENODEV;var = info->var;            //获取 fb_info 中的参数unlock_fb_info(info);ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0;//将 fb_info 中的参数传给应用break;}
}

三、总结


1、fbmem.c 文件:搭建了LCD驱动框架


1)创建 /proc/fb 文件操作接口;

2)构造 fb_fops(file_operation),register_chrdev 注册字符设备驱动;

3)class_create创建类 "graphics"(由设备相关驱动创建设备节点)

2、mxsfb.c 文件:底层硬件层


匹配过程就是platform设备驱动那套流程。设备树(platform_device)与 mxsfb.c(platform_driver) 匹配后,调用 .probe。这里 .probe就是 mxsfb_probe() 函数。

mxsfb_probe() 函数主要功能

1)framebuffer_alloc()分配一个fb_info

2)设置fb_info,如:fb_info->fbops = &mxsfb_ops; 设置操作集

3)硬件相关寄存器配置

4)register_framebuffer()注册fb_info

register_framebuffer()功能:

1)device_create() 创建设备,生成 /dev/fb0

2)registered_fb[i] = fb_info; //将fb_info放入空的registered_fb[i]位置,i设为次设备号

应用程序open,会获取 registered_fb[i] 中对应次设备号的 fb_info,并调用它的open函数。

由此可见,fb_info结构非常重要

四、应用层如何使用 /dev/fb0


  • open函数:用于打开Framebuffer设备文件(如/dev/fb0)。
  • ioctl函数:用于获取和设置Framebuffer的参数,如分辨率、颜色深度等。
  • mmap函数:用于将Framebuffer映射到进程的地址空间,以便进行读写操作。
#include "framebuffer.h"
#include <linux/fb.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <math.h>
void *pmem;
struct fb_var_screeninfo vinf;int init_fb(char *devname)
{//1. 打开显示设备int fd = open(devname, O_RDWR);	if (-1 == fd){perror("fail open fb");return -1;}//2、获取显示设备相关参数 分辨率 位深度int ret = ioctl(fd, FBIOGET_VSCREENINFO, &vinf);if (-1 ==ret){perror("fail ioctl");return -1;}printf("xres = %d, yres = %d\n", vinf.xres, vinf.yres);printf("xres_virtual = %d, yres_virtual = %d\n", vinf.xres_virtual, vinf.yres_virtual);printf("bits_per_pixel : %d\n", vinf.bits_per_pixel);size_t len = vinf.xres_virtual * vinf.yres_virtual * vinf.bits_per_pixel/8;//3, 建立显存和用户空间的映射关系pmem = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);if ((void *)-1 == pmem){perror("fail mmap");return -1;}return fd;
}void draw_point(int x, int y, unsigned int col)
{if (x >= vinf.xres || y >= vinf.yres){return ;}if (vinf.bits_per_pixel == RGB888_FMT){unsigned int *p = (unsigned int *)pmem;*(p + y * vinf.xres_virtual + x) = col;}else if (vinf.bits_per_pixel == RGB565_FMT){unsigned short *p  = (unsigned short *)pmem;	*(p + y * vinf.xres_virtual + x) = col;}return ;
}
void draw_x_line(int x, int y, int len, unsigned int col)
{if (x >= vinf.xres || y >= vinf.yres){return ;}for (int i = x; i < x+len; i++){draw_point(i, y, col);}return;
}
void draw_y_line(int x, int y, int len, unsigned int col)
{if (x >= vinf.xres || y >= vinf.yres){return ;}for (int i = y; i < y+len; i++){draw_point(x, i, col);}return;
}void draw_bias(int x, int y, int len, unsigned int col)
{if (x >= vinf.xres || y >= vinf.yres){return ;}int i=0;while(i<len){   draw_point(x, y, col);++x;++y;++i;}
}
void draw_circle(int x, int y, int r, unsigned int col)
{int x0, y0;for (int i = 0; i <= 360; ++i){x0 = r * cos(2 * 3.1415/360 * i) + x;y0 = r * sin(2 * 3.1415/360 * i) + y;draw_point(x0, y0, col);draw_point(x0+1, y0, col);draw_point(x0, y0+1, col);draw_point(x0-1, y0, col);draw_point(x0, y0-1, col);}
}void draw_rectangle(int x,int y,int len,int wide,unsigned int col)
{if (x >= vinf.xres || y >= vinf.yres){return ;}draw_x_line(x,y,len,col);draw_y_line(x,y,wide,col);draw_x_line(x,y+wide,len,col);draw_y_line(x+len,y,wide,col);
}void draw_clear(unsigned int col)
{for(int i =0;i<vinf.xres_virtual;++i){for(int j=0;j<vinf.yres_virtual;++j){draw_point(i,j,col);}}
}
void uninit_fb(int fd)
{size_t len = vinf.xres_virtual * vinf.yres_virtual * vinf.bits_per_pixel/8;munmap(pmem, len);close(fd);
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词