1. framebuffer简介
1.1. 裸机中如何操作LCD?
1.1.1 驱动器&控制器
(1)LCD驱动器一般和LCD显示面板集成在一起(本来是分开的,做面板的是只做面板的,譬如说三星、LG、台湾的友达、奇美都是做面板的;驱动器也由专门的IC厂商生 产;集成厂商买来面板和驱动器后集成在一起做成LCD屏幕),面板只负责里面的液晶分子旋转透光,面板需要一定的模拟电信号来控制液晶分子;LCD驱动器芯片负责给 面板提供控制液晶分子的模拟电信号,驱动器的控制信号(数字信号)来自于自己的数字接口,这个接口就是LCD屏幕的外部接口。
(2)LCD控制器一般集成在SoC内部,他负责通过数字接口向远端的LCD驱动器提供控制像素显示的数字信号。LCD控制器的关键在于时序,它必须按照一定的时序和LCD驱 动器通信;LCD控制器受SoC控制,SoC会从内存中拿像素数据给LCD控制器并最终传给LCD驱动器。
1.1.2. 显示内存(简称:显存)
(1)SoC在内存中挑选一段内存(一般来说是程序员随便挑选的,但是挑选的时候必须符合一定规矩),然后通过配置将LCD控制器和这一段内存(以后称为显存)连接起来 构成一个映射关系。一旦这个关系建立之后,LCD控制器就会自动从显存中读取像素数据传输给LCD驱动器。这个显示的过程不需要CPU的参与。
(2)显示体系建立起来后,CPU就不用再管LCD控制器、驱动器、面板这些东西了;以后CPU就只关心显存了,因为我只要把要显示的图像的像素数据丢到显存中,硬件就 会自动响应(屏幕上就能自动看到显示的图像了)。
总结:LCD显示是分为2个阶段的:第一个阶段就是建立显示体系的过程,目的就是CPU初始化LCD控制器使其和显存联系起来构成映射;第二个阶段就是映射建立之后, 此阶段主要任务是将要显示的图像丢到显存中去。
1.2. 什么是framebuffer?
(1)framebuffer帧缓冲(一屏幕数据)(简称fb)是linux内核中虚拟出的一个设备,framebuffer向应用层提供一个统一标准接口的显示设备。帧缓冲(framebuffer)是Linux为显示设备提供的一个接口,把显存抽象后的一种设备,他允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。这种操作是抽象的,统一的。用户不必关心物理显存的位置、换页机制等等具体细节。这些都是由Framebuffer设备驱动来完成的。
(2)从驱动来看,fb是一个典型的字符设备,而且创建了一个类/sys/class/graphics
(3)framebuffer的使用
- 打开framebuffer设备文件: /dev/fb0
- 获取framebuffer设备信息 #include <linux/fb.h>
- mmap做映射
- 填充framebuffer
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
// 宏定义
#define FBDEVICE "/dev/fb0"
// 新开发板
#define WIDTH 1024
#define HEIGHT 600
#define WHITE 0xffffffff
#define BLACK 0x00000000
#define RED 0xffff0000
#define GREEN 0xff00ff00
#define BLUE 0xff0000ff
// 函数声明
void draw_back(unsigned int width, unsigned int height, unsigned int color);
void draw_line(unsigned int color);
// 全局变量
unsigned int *pfb = NULL;
int main(void)
{
int fd = -1, ret = -1;
struct fb_fix_screeninfo finfo = {0};
struct fb_var_screeninfo vinfo = {0};
// 第1步:打开设备
fd = open(FBDEVICE, O_RDWR);
if (fd < 0)
{
perror("open");
return -1;
}
printf("open %s success.\n", FBDEVICE);
// 第2步:获取设备的硬件信息
ret = ioctl(fd, FBIOGET_FSCREENINFO, &finfo);
if (ret < 0)
{
perror("ioctl");
return -1;
}
printf("smem_start = 0x%x, smem_len = %u.\n", finfo.smem_start, finfo.smem_len);
ret = ioctl(fd, FBIOGET_VSCREENINFO, &vinfo); //获取可变信息
if (ret < 0)
{
perror("ioctl");
return -1;
}
printf("xres = %u, yres = %u.\n", vinfo.xres, vinfo.yres);
printf("xres_virtual = %u, yres_virtual = %u.\n", vinfo.xres_virtual, vinfo.yres_virtual);
printf("bpp = %u.\n", vinfo.bits_per_pixel);
// 第3步:进行mmap
unsigned long len = vinfo.xres_virtual * vinfo.yres_virtual * vinfo.bits_per_pixel / 8;
printf("len = %ld\n", len);
pfb = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (NULL == pfb)
{
perror("mmap");
return -1;
}
printf("pfb = %p.\n", pfb);
draw_back(WIDTH, HEIGHT, WHITE);
draw_line(RED);
close(fd);
return 0;
}
void draw_back(unsigned int width, unsigned int height, unsigned int color)
{
unsigned int x, y;
for (y=0; y<height; y++)
{
for (x=0; x<width; x++)
{
*(pfb + y * WIDTH + x) = color;
}
}
}
void draw_line(unsigned int color)
{
unsigned int x, y;
for (x=50; x<600; x++)
{
*(pfb + 200 * WIDTH + x) = color;
}
}
(4)FB驱动框架相关代码:drivers\video 这个目录中
2. 相关的数据结构
struct fb_info { // 用来描述一个fb设备的结构体
int node; // 用来表示该fb设备的次设备号
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 */ // fb的可变参数
struct fb_fix_screeninfo fix; /* Current fix */ // fb的不可变参数
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_IO
struct delayed_work deferred_work;
struct fb_deferred_io *fbdefio;
#endif
struct fb_ops *fbops; // 该设备对应的操作方法 open write read .....
struct device *device; /* This is the parent */ // fb设备的父设备
struct device *dev; /* This is this fb device */ // 本设备的device
int class_flag; /* private sysfs flags */
#ifdef CONFIG_FB_TILEBLITTING
struct fb_tile_ops *tileops; /* Tile Blitting */
#endif
char __iomem *screen_base; /* Virtual address */ // 这个就是我们的LCD的显存地址(虚拟地址)
unsigned long screen_size; /* Amount of ioremapped VRAM or 0 */ // LCD显存的字节大小
void *pseudo_palette; /* Fake palette of 16 colors */
#define FBINFO_STATE_RUNNING 0
#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 */
struct apertures_struct {
unsigned int count;
struct aperture {
resource_size_t base;
resource_size_t size;
} ranges[0];
} *apertures;
};
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 colors */
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 */ // LCD的物理高度mm
__u32 width; /* width of picture in mm */ // LCD的物理宽度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) */ // 像素时钟
// 下面这六个就是LCD的时序参数
__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 */
};
struct fb_fix_screeninfo {
char id[16]; /* identification string eg "TT Builtin" */
unsigned long smem_start; /* Start of frame buffer mem */ // LCD显存的起始地址(物理地址)
/* (physical address) */
__u32 smem_len; /* Length of frame buffer mem */ // LCD显存的字节大小
__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 */ // LCD一行的长度 (以字节为单位)
unsigned long mmio_start; /* Start of Memory Mapped I/O */
/* (physical address) */
__u32 mmio_len; /* Length of Memory Mapped I/O */
__u32 accel; /* Indicate to driver which */
/* specific chip/card we have */
__u16 reserved[3]; /* Reserved for future compatibility */
};
struct fb_pixmap {
u8 *addr; /* pointer to memory */
u32 size; /* size of buffer in bytes */
u32 offset; /* current offset to buffer */
u32 buf_align; /* byte alignment of each bitmap */
u32 scan_align; /* alignment per scanline */
u32 access_align; /* alignment per read/write (bits) */
u32 flags; /* see FB_PIXMAP_* */
u32 blit_x; /* supported bit block dimensions (1-32)*/
u32 blit_y; /* Format: blit_x = 1 << (width - 1) */
/* blit_y = 1 << (height - 1) */
/* if 0, will be set to 0xffffffff (all)*/
/* access methods */
void (*writeio)(struct fb_info *info, void __iomem *dst, void *src, unsigned int size);
void (*readio) (struct fb_info *info, void *dst, void __iomem *src, unsigned int size);
};
struct fb_videomode {
const char *name; /* optional */
u32 refresh; /* optional */
u32 xres;
u32 yres;
u32 pixclock;
u32 left_margin;
u32 right_margin;
u32 upper_margin;
u32 lower_margin;
u32 hsync_len;
u32 vsync_len;
u32 sync;
u32 vmode;
u32 flag;
};
struct fb_ops { // 这个fb_ops 就是我们fb设备的使用的 fops
/* 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 */
int (*fb_set_par)(struct fb_info *info);
/* set color register */
int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,
unsigned blue, unsigned transp, struct fb_info *info);
/* 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);
};
3. 函数分析
framebuffer驱动框架部分的代码与前面说的misc驱动框架和led驱动框架一样,都是实现为一个模块的形式,可以在内核配置的时候进行动态的加载和卸载。
3.1 fbmem_init
static int __init
fbmem_init(void)
{
proc_create("fb", 0, NULL, &fb_proc_fops); // 在proc文件系统中建立一个名为 fb 的目录
if (register_chrdev(FB_MAJOR,"fb",&fb_fops)) // 注册字符设备 fb 主设备号29 fb_fops
printk("unable to get major %d for fb devs\n", FB_MAJOR);
fb_class = class_create(THIS_MODULE, "graphics"); // 创建设备类 /sys/class/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;
}
(1)fb_fops变量
fb_fops是一个struct file_operations结构体类型的变量,这个结构体不是什么新鲜玩样了,framebuffer驱动框架中注册的这个file_operations结构体内容如下:
static const struct file_operations fb_fops = {
.owner = THIS_MODULE,
.read = fb_read, //read函数
.write = fb_write, //write函数
.unlocked_ioctl = fb_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = fb_compat_ioctl,
#endif
.mmap = fb_mmap, //mmap函数
.open = fb_open, //open函数
.release = fb_release, //close函数
#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
};
(2)对fb_fops结构体中的fb_open函数分析:
fb_open(struct inode *inode, struct file *file) (以下层次为函数的调用关系)
int fbidx = iminor(inode); // 通过inode指针获取设备的次设备号
struct fb_info *info;
info = registered_fb[fbidx]; // 找到以次设备号为下标的数组项
info->fbops->fb_open(info,1); // 调用info指向的fb_info结构体中的fb_ops结构体中的open函数(注意:fbops指针是一个fb_ops类型的结构体指针,上面说的fb_fops 是file_operations类型的结构体的变量)
1):注意:上面分析的只是open函数,那么其他的函数也是一样的原理,最终都会调用到驱动编写者向框架注册的fb_info结构体中的fb_fops中封装的函数。
2):从这里可以看出来fb的驱动框架注册的file_operations与之前讲的misc驱动框架中注册file_operations有所不同,他们的不同主要两个:
- misc中注册的file_operations结构体中只有open函数,都是通过这个open函数获取设备的file_operations;而fb驱动框架中注册的file_operations结构体中基本函数都实现了,真正驱动中的不同的函数是通过这个结构体中对应的函数内部进行转换的。
- misc驱动框架下的具体驱动的操作函数也是封装在一个file_operations结构体变量中,当然这个变量是在struct miscdevice结构体中包含的;而fb驱动框架下的具体的驱动的操作函数是封装在一个fb_ops类型的结构体变量中的,是包含在fb_info结构体中,随着fb_info变量的注册而向驱动框架注册。
3.2 register_framebuffer
这是驱动框架留给驱动工程师的接口,驱动框架代码是不涉及到具体的硬件操作的,主要是软件逻辑,提供服务性的代码。驱动工程师需要调用驱动框架提供的接口函数来向驱动框架注册驱动、设备。struct fb_info结构体是驱动编写者利用fb驱动框架来编写fb驱动时需要提供的一个结构体,内部封装了很多的信息,包括LCd的硬件信息,像素、像素深度、尺寸大小等等。其中重要的有struct fb_fix_screeninfo(可变参数)、struct fb_var_screeninfo(不可变参数)、struct fb_videomode(显示模式:显示分辨率、刷新率等的)、struct fb_ops(这个结构体封装了open、read、write....等应用层对应到驱动的函数)。
int
register_framebuffer(struct fb_info *fb_info)
{
int i;
struct fb_event event; // 定义一个fb_event 变量
struct fb_videomode mode; // 定义一个fb_videomode 变量
if (num_registered_fb == FB_MAX) // 如果当前注册的fb设备的数量已经满了,则不能在进行注册了 最大值32
return -ENXIO;
if (fb_check_foreignness(fb_info)) // 校验
return -ENOSYS;
remove_conflicting_framebuffers(fb_info->apertures, fb_info->fix.id, // 防冲突检查
fb_is_primary_device(fb_info));
num_registered_fb++; // 当前注册的设备数量 + 1
for (i = 0 ; i < FB_MAX; i++) // registered_fb 是fb驱动框架维护的一个用来管理记录fb设备的数组, 里面的元素就是 fb_info 指针,一个fb_info就代表一个fb设备
if (!registered_fb[i]) // 找到一个最小的没有被使用的次设备号
break;
fb_info->node = i; // 将次设备号存放在 fb_info->node 中
mutex_init(&fb_info->lock);
mutex_init(&fb_info->mm_lock);
fb_info->dev = device_create(fb_class, fb_info->device, // 创建设备 名字: fb+次设备号
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); // 初始化fb设备
if (fb_info->pixmap.addr == NULL) { // pixmap 相关的设置
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); // 初始化链表
fb_var_to_videomode(&mode, &fb_info->var); // 从fb_info结构体中获取显示模式存放在mode变量中
fb_add_videomode(&mode, &fb_info->modelist); // 添加显示模式: 先检查需要添加的显示模式是否在链表中已经存在,如果存在则没有必要在进行添加
registered_fb[i] = fb_info; // 将fb_info 结构体存放到 registered_fb 驻足中去
event.info = fb_info;
if (!lock_fb_info(fb_info))
return -ENODEV;
fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event); // 异步通知: 通知那些正在等待fb注册事件发生的进程
unlock_fb_info(fb_info);
return 0;
}
3.2.1 fb_init_device函数分析:
int fb_init_device(struct fb_info *fb_info)
{
int i, error = 0;
dev_set_drvdata(fb_info->dev, fb_info); // 设置fb设备的私有数据中的设备驱动私有数据,也就是将 fb_info 存放在 dev->p->driver_data
fb_info->class_flag |= FB_SYSFS_FLAG_ATTR; // 设置标志位
for (i = 0; i < ARRAY_SIZE(device_attrs); i++) { // 根据 device_attrs 这个device属性数组来创建设备属性文件
error = device_create_file(fb_info->dev, &device_attrs[i]);
if (error)
break;
}
if (error) {
while (--i >= 0)
device_remove_file(fb_info->dev, &device_attrs[i]);
fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR;
}
return 0;
}
3.2.2 设备属性相关结构体如下所示:
static struct device_attribute device_attrs[] = {
__ATTR(bits_per_pixel, S_IRUGO|S_IWUSR, show_bpp, store_bpp), //查看/修改fb设备的像素深度
__ATTR(blank, S_IRUGO|S_IWUSR, show_blank, store_blank),
__ATTR(console, S_IRUGO|S_IWUSR, show_console, store_console),
__ATTR(cursor, S_IRUGO|S_IWUSR, show_cursor, store_cursor),
__ATTR(mode, S_IRUGO|S_IWUSR, show_mode, store_mode), //查看/修改fb设备的显示模式
__ATTR(modes, S_IRUGO|S_IWUSR, show_modes, store_modes),
__ATTR(pan, S_IRUGO|S_IWUSR, show_pan, store_pan),
__ATTR(virtual_size, S_IRUGO|S_IWUSR, show_virtual, store_virtual), //查看/修改虚拟像素大小
__ATTR(name, S_IRUGO, show_name, NULL),
__ATTR(stride, S_IRUGO, show_stride, NULL),
__ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),
__ATTR(state, S_IRUGO|S_IWUSR, show_fbstate, store_fbstate),
#ifdef CONFIG_FB_BACKLIGHT
__ATTR(bl_curve, S_IRUGO|S_IWUSR, show_bl_curve, store_bl_curve),
#endif
};
4. 总结:framebuffer驱动框架总览
fb的驱动框架代码主要涉及到以下的4个文件:
(1)drivers/video/fbmem.c。主要任务:1、创建graphics类、注册FB的字符设备驱动、提供register_framebuffer接口给具体framebuffer驱动编写着来注册fb设备的。
本文件相对于fb来说,地位和作用和misc.c文件相对于杂散类设备来说一样的,结构和分析方法也是类似的。
(2)drivers/video/fbsys.c。这个文件是处理fb在/sys目录下的一些属性文件的,例如register_framebuffer函数中fb_init_device函数就是存在这个文件中
(3)drivers/video/modedb.c。这个文件是管理显示模式(譬如VGA、720P等就是显示模式)的
(4)drivers/video/fb_notify.c。异步通知函数,例如fb_notifier_call_chain函数就在这个文件中