1.fb_info 结构体
帧缓冲设备最关键的一个数据结构体是fb_info 结构体(为了便于记忆,我们把它
简称为“FBI”),FBI中包括了
关于帧缓冲设备属性和操作的完整描述
,这个结构体的
定义如代码清单 所示。
1 struct fb_info
2 {
3 int node;
4 int flags;
5 struct fb_var_screeninfo var; /*可变参数 */
6 struct fb_fix_screeninfo fix; /*固定参数 */
7 struct fb_monspecs monspecs; /*显示器标准 */
8 struct work_struct queue; /* 帧缓冲事件队列 */
9 struct fb_pixmap pixmap; /* 图像硬件mapper */
10 struct fb_pixmap sprite; /* 光标硬件mapper */
11 struct fb_cmap cmap; /* 目前的颜色表*/
12 struct list_head modelist;
13 struct fb_videomode *mode; /* 目前的video模式 */
14
15 #ifdef CONFIG_FB_BACKLIGHT
16 struct mutex bl_mutex;
17 /* 对应的背光设备 */
18 struct backlight_device *bl_dev;
19 /* 背光调整 */
20 u8 bl_curve[FB_BACKLIGHT_LEVELS];
21 #endif
22
23 struct fb_ops *fbops; /* fb_ops,帧缓冲操作 */
24 struct device *device;
25 struct class_device *class_device; /
26 int class_flag; /* 私有sysfs标志 */
27 #ifdef CONFIG_FB_TILEBLITTING
28 struct fb_tile_ops *tileops; /* 图块Blitting */
29 #endif
30 char _ _iomem *screen_base; /* 虚拟基地址 */
31 unsigned long screen_size; /* ioremapped的虚拟内存大小 */
32 void *pseudo_palette; /* 伪16色颜色表 */
33 #define FBINFO_STATE_RUNNING 0
34 #define FBINFO_STATE_SUSPENDED 1
35 u32 state; /* 硬件状态,如挂起 */
36 void *fbcon_par;
37 void *par;
38 };
2、fb_ops
FBI的成员变量fbops为指向底层操作的函数的指针,这些函数是需要驱动程序
发人员编写的
,其定义如代码清单18.2 所示。
1 struct fb_ops
2 {
3 struct module *owner;
4 /* 打开/释放 */
5 int(*fb_open)(struct fb_info *info, int user);
6 int(*fb_release)(struct fb_info *info, int user);
7
8 /* 对于 非线性布局的/常规内存映射无法工作的 帧缓冲设备需要 */
9 ssize_t(*fb_read)(struct file *file, char _ _user *buf, size_t count,
10 loff_t*ppos);
11 ssize_t(*fb_write)(struct file *file, const char _ _user *buf, size_t count,
12 loff_t *ppos);
13
14 /* 检测可变参数,并调整到支持的值*/
15 int(*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);
16
17 /* 根据info->var设置video模式 */
18 int(*fb_set_par)(struct fb_info *info);
19
20 /* 设置color寄存器 */
21 int(*fb_setcolreg)(unsigned regno, unsigned red, unsigned green, unsigned
22 blue, unsigned transp, struct fb_info *info);
23
24 /* 批量设置color寄存器,设置颜色表 */
25 int(*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);
26
27 /*显示空白 */
28 int(*fb_blank)(int blank, struct fb_info *info);
29
30 /* pan显示 */
31 int(*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);
32
33 /* 矩形填充 */
34 void(*fb_fillrect)(struct fb_info *info, const struct fb_fillrect *rect);
35 /* 数据复制 */
36 void(*fb_copyarea)(struct fb_info *info, const struct fb_copyarea *region);
37 /* 图形填充 */
38 void(*fb_imageblit)(struct fb_info *info, const struct fb_image *image);
39
40 /* 绘制光标 */
41 int(*fb_cursor)(struct fb_info *info, struct fb_cursor *cursor);
42
43 /* 旋转显示 */
44 void(*fb_rotate)(struct fb_info *info, int angle);
45
46 /* 等待blit空闲 (可选) */
47 int(*fb_sync)(struct fb_info *info);
48
49 /* fb特定的ioctl (可选) */
50 int(*fb_ioctl)(struct fb_info *info, unsigned int cmd, unsigned long arg);
51
52 /* 处理32位的compat ioctl (可选) */
53 int(*fb_compat_ioctl)(struct fb_info *info, unsigned cmd, unsigned long arg);
54
55 /* fb特定的mmap */
56 int(*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);
57
58 /* 保存目前的硬件状态 */
59 void(*fb_save_state)(struct fb_info *info);
60
61 /* 恢复被保存的硬件状态 */
62 void(*fb_restore_state)(struct fb_info *info);
63 };
3.fb_var_screeninfo 和fb_fix_screeninfo 结构体
FBI的fb_var_screeninfo 和fb_fix_screeninfo 成员也是结构体,
fb_var_screeninfo
记录用户可修改的显示控制器参数
,
包括屏幕分辨率和每个像素点的比特数
。
fb_var_screeninfo中的xres定义屏幕一行有多少个点,yres定义屏幕一列有多少个点,
bits_per_pixel定义每个点用多少个字节表示。
而fb_fix_screeninfo 中记录用户不能修
改的显示控制器的参数
,如
屏幕缓冲区的物理地址、长度
。
当对帧缓冲设备进行映射
操作的时候,就是从fb_fix_screeninfo 中取得缓冲区物理地址的。上述数据成员都需
要在驱动程序中初始化和设置。
fb_var_screeninfo和fb_fix_screeninfo结构体的定义分别如代码清单18.3 和代码清
单18.4所示。
代码清单18.3 fb_var_screeninfo结构体
1 struct fb_var_screeninfo
2 {
3 /* 可见解析度 */
4 _ _u32 xres;
5 _ _u32 yres;
6 /* 虚拟解析度 */
7 _ _u32 xres_virtual;
8 _ _u32 yres_virtual;
9 /* 虚拟到可见之间的偏移 */
10 _ _u32 xoffset;
11 _ _u32 yoffset;
12
13 _ _u32 bits_per_pixel; /* 每像素位数,BPP */
14 _ _u32 grayscale; /非0时指灰度 */
15
16 /* fb缓存的R\G\B位域 */
17 struct fb_bitfield red;
18 struct fb_bitfield green;
19 struct fb_bitfield blue;
20 struct fb_bitfield transp; /* 透明度 */
21
22 _ _u32 nonstd; /* != 0 非标准像素格式 */
23
24 _ _u32 activate;
25
26 _ _u32 height; /*高度 */
27 _ _u32 width; /*宽度 */
28
29 _ _u32 accel_flags; /* 看fb_info.flags */
30
31 /* 定时: 除了pixclock本身外,其他的都以像素时钟为单位 */
32 _ _u32 pixclock; /* 像素时钟(皮秒) */
33 _ _u32 left_margin; /* 行切换:从同步到绘图之间的延迟 */
34 _ _u32 right_margin; /* 行切换:从绘图到同步之间的延迟 */
35 _ _u32 upper_margin; /* 帧切换:从同步到绘图之间的延迟 */
36 _ _u32 lower_margin; /* 帧切换:从绘图到同步之间的延迟 */
37 _ _u32 hsync_len; /* 水平同步的长度 */
38 _ _u32 vsync_len; /* 垂直同步的长度 */
39 _ _u32 sync;
40 _ _u32 vmode;
41 _ _u32 rotate; /* 顺时钟旋转的角度 */
42 _ _u32 reserved[5]; /* 保留 */
43 };
代码清单18.4 fb_fix_screeninfo结构体
1 struct fb_fix_screeninfo
2 {
3 char id[16]; /* 字符串形式的标识符 */
4 unsigned long smem_start; /* fb缓存的开始位置 */
5 _ _u32 smem_len; /* fb缓存的长度 */
6 _ _u32 type; /* FB_TYPE_* */
7 _ _u32 type_aux; /* 分界 */
8 _ _u32 visual; /* FB_VISUAL_* */
9 _ _u16 xpanstep; /* 如果没有硬件panning ,赋0 */
10 _ _u16 ypanstep;
11 _ _u16 ywrapstep;/
12 _ _u32 line_length; /* 1行的字节数 */
13 unsigned long mmio_start; /* 内存映射I/O的开始位置 */
14 _ _u32 mmio_len; /* 内存映射I/O的长度 */
15 _ _u32 accel;
16 _ _u16 reserved[3]; /* 保留*/
17 };
代码清单18.4 中第8行的 visual记录屏幕使用的色彩模式,在Linux系统中,支
持的
色彩模式
包括如下几种。
l Monochrome(FB_VISUAL_MONO01、FB_VISUAL_MONO10),每个像素
是黑或白。
l Pseudo color ( FB_VISUAL_PSEUDOCOLOR 、
FB_VISUAL_STATIC_PSEUDOCOLOR),即伪彩色,采用索引颜色显示。
l True color(FB_VISUAL_TRUECOLOR),真彩色,分成红、绿、蓝三基色。
l Direct color(FB_VISUAL_DIRECTCOLOR),每个像素颜色也是有红、绿、
蓝组成,不过每个颜色值是个索引,需要查表。
l Grayscale displays, 灰度显示,红、绿、蓝的值都一样。
4.fb_bitfield 结构体
代码清单18.3第17、18、19行分别记录R、G、B的位域,fb_bitfield结构体描
述每一像素显示缓冲区的组织方式,包含位域偏移、位域长度和MSB指示,如代码
清单18.5所示。
代码清单18.5 fb_bitfield结构体
1 struct fb_bitfield
2 {
3 _ _u32 offset; /* 位域偏移 */
4 _ _u32 length; /* 位域长度 */
5 _ _u32 msb_right; /* MSB */
6 };
5.fb_cmap结构体
fb_cmap 结构体记录设备无关的颜色表信息,用户空间可以通过ioctl()的
FBIOGETCMAP 和FBIOPUTCMAP命令读取或设定颜色表。
代码清单18.6 fb_cmap结构体
1 struct fb_cmap
2 {
3 _ _u32 start; /* 第1个元素入口 */
4 _ _u32 len; /* 元素数量 */
5 /* R、G、B、透明度*/
6 _ _u16 *red;
7 _ _u16 *green;
8 _ _u16 *blue;
9 _ _u16 *transp;
10 };
代码清单18.7所示为用户空间获取颜色表的例程,若BPP为8位,则颜色表长
度为256;若BPP为4位,则颜色表长度为16;否则,颜色表长度为0,这是因为,
对于BPP大于等于16的情况,使用颜色表是不划算的。
代码清单18.7 用户空间获取颜色表例程
1 // 读入颜色表
2 if ((vinfo.bits_per_pixel == 8) || (vinfo.bits_per_pixel == 4))
3 {
4 screencols = (vinfo.bits_per_pixel == 8) ? 256 : 16; //颜色表大小
5 int loopc;
6 startcmap = new fb_cmap;
7 startcmap->start = 0;
8 startcmap->len = screencols;
9 //分配颜色表的内存
10 startcmap->red = (unsigned short int*)malloc(sizeof(unsigned short int)
11 *screencols);
12 startcmap->green = (unsigned short int*)malloc(sizeof(unsigned short int)
13 *screencols);
14 startcmap->blue = (unsigned short int*)malloc(sizeof(unsigned short int)
15 *screencols);
16 startcmap->transp = (unsigned short int*)malloc(sizeof(unsigned short int)
17 *screencols);
18 //获取颜色表
19 ioctl(fd, FBIOGETCMAP, startcmap);
20 for (loopc = 0; loopc < screencols; loopc++)
21 {
22 screenclut[loopc] = qRgb(startcmap->red[loopc] >> 8, startcmap
23 ->green[loopc] >> 8, startcmap->blue[loopc] >> 8);
24 }
25 }
26 else
27 {
28 screencols = 0;
29 }
对于一个256色(BPP=8)的800*600分辨率的图像而言,若红、绿、蓝分别
用一个字节描述,则需要800*600*3=1440000Byte 的空间,而若使用颜色表,则只
需要800*600*1+256*3= 480768Byte 的空间。
6.文件操作结构体 对比 帧缓冲操作函数函数 fb_ops, 在s3cfb.c中由程序员实现
作为一种字符设备,帧缓冲设备的文件操作结构体定义于
/linux/drivers/vedio/fbmem.c 文件中,如代码清单18.8 所示。
代码清单18.8 帧缓冲设备文件操作结构体
1 static struct file_operations fb_fops =
2 {
3 .owner = THIS_MODULE,
4 .read = fb_read, //读函数
5 .write = fb_write, //写函数
6 .ioctl = fb_ioctl, //I/O控制函数
7 #ifdef CONFIG_COMPAT
8 .compat_ioctl = fb_compat_ioctl,
9 #endif
10 .mmap = fb_mmap, //内存映射函数
11 .open = fb_open, //打开函数
12 .release = fb_release, //释放函数
13 #ifdef HAVE_ARCH_FB_UNMAPPED_AREA
14 .get_unmapped_area = get_fb_unmapped_area,
15 #endif
16 };
帧缓冲设备驱动的文件操作接口函数已经在fbmem.c 中被统一实现,一般不需要
由驱动工程师再编写
。
7.注册与注销帧缓冲设备
Linux内核提供了register_framebuffer()和unregister_framebuffer()函数分别注册和
注销帧缓冲设备,这两个函数都接受FBI指针为参数,原型为:
int register_framebuffer(struct fb_info *fb_info);
int unregister_framebuffer(struct fb_info *fb_info);
对于register_framebuffer()函数而言,如果注册的帧缓冲设备数超过了FB_MAX
(目前定义为32),则函数返回-ENXIO,注册成功则返回0。
Linux帧缓冲设备驱动结构
图18.3 所示为Linux 帧缓冲设备驱动的主要结构,帧缓冲设备提供给用户空间的
file_operations结构体由fbmem.c 中的file_operations提供,而特定帧缓冲设备fb_info
结构体的注册、注销以及其中成员的维护,尤其是fb_ops中成员函数的实现则由对应
的xxxfb.c文件实现,fb_ops中的成员函数最终会操作LCD控制器硬件寄存器。
图18.3 帧缓冲设备驱动的程序结构
帧缓冲设备驱动的模块加载与卸载函数
在帧缓冲设备驱动的模块加载函数中,应该完成如下4个工作。
1、 申请FBI结构体的内存空间,初始化FBI结构体中 固定和可变的屏幕参数,
即填充FBI中fb_var_screeninfo var和struct fb_fix_screeninfo fix成员。
2、 根据具体LCD屏幕的特点,完成LCD控制器硬件的初始化。
3、 申请帧缓冲设备的显示缓冲区空间。
4、 注册帧缓冲设备。
在帧缓冲设备驱动的模块卸载函数中,应该完成相反的工作,包括释放FBI结构
体内存、关闭LCD、释放显示缓冲区以及注销帧缓冲设备。
由于LCD控制器经常被集成在SoC 上作为一个独立的硬件模块而存在(成为
platform_device),因此,LCD驱动中也经常包含
平台驱动
,
这样,
在帧缓冲设备驱动
的模块加载函数中完成的工作只是注册平台驱动,
而初始化FBI结构体中的固定和可变
参数、LCD控制器硬件的初始化、申请帧缓冲设备的显示缓冲区空间和注册帧缓冲设
备的工作则移交到平台驱动的探测函数中完成。
同样地,在使用平台驱动的情况下,释放FBI结构体内存、关闭LCD、释放显示
缓冲区以及注销帧缓冲设备的工作也移交到平台驱动的移除函数中完成。
代码清单18.9 所示为帧缓冲设备驱动的模块加载和卸载以及平台驱动的探测和移除函
数中的模板。
帧缓冲设备显示缓冲区的申请与释放
在嵌入式系统中,一种常见的方式是直接在RAM空间中分配一段显示缓冲区,
典型结构如图18.4所示。
在分配显示缓冲区时一定要考虑cache的一致性问题,因为系统往往会通过
DMA方式搬移显示数据。合适的方式是使用dma_alloc_writecombine()函数分配
一段writecombining 区域,对应的writecombining 区域由
dma_free_writecombine()函数释放,如代码清单18.10所示。
1 static int __init xxxfb_map_video_memory(struct xxxfb_info *fbi)
2 {
3 fbi->map_size = PAGE_ALIGN(fbi->fb->fix.smem_len + PAGE_SIZE);
4 fbi->map_cpu = dma_alloc_writecombine(fbi->dev, fbi->map_size,
5 &fbi->map_dma,GFP_KERNEL); //分配内存
6
7 fbi->map_size = fbi->fb->fix.smem_len; //显示缓冲区大小
8
9 if (fbi->map_cpu)
10 {
11 memset(fbi->map_cpu, 0xf0, fbi->map_size);
12
13 fbi->screen_dma = fbi->map_dma;
14 fbi->fb->screen_base = fbi->map_cpu;
15 fbi->fb->fix.smem_start = fbi->screen_dma; //赋值fix 的smem_start
16 }
17
18 return fbi->map_cpu ? 0 : - ENOMEM;
19 }
20
21 static inline void xxxfb_unmap_video_memory(struct s3c2410fb_info *fbi)
22 {
23 //释放显示缓冲区
24 dma_free_writecombine(fbi->dev,fbi->map_size,fbi->map_cpu, fbi->map_dma);
25 }
帧缓冲设备的参数设置
18.6.1 定时参数
FBI 结构体可变参数var 中的left_margin、right_margin、upper_margin、
lower_margin、hsync_len和vsync_len直接查LCD的数据手册就可以得到,图18.5所
示为某LCD数据手册中直接抓图获得的定时信息。
由图18.5可知对该LCD而言,var中各参数的较合适值分别为:left_margin = 104,
right_margin = 8,upper_margin = 2,lower_margin = 2,hsync_len = 2,vsync_len = 2。
在QVGA模式下也可类似得到。
18.6.2 像素时钟
FBI可变参数var中的pixclock意味着像素时钟,例如,如果为28.37516 MHz,
那么画1个像素需要
35242 ps(皮秒):
1/(28.37516E6 Hz) = 35.242E-9 s
如果屏幕的分辨率是 640×480,显示一行需要的时间是:
640*35.242E-9 s = 22.555E-6 s
每条扫描线是640,但是水平回扫和水平同步也需要时间,假设水平回扫和同步
需要272个像素时钟,因此,画一条扫描线完整的时间是:
(640+272)*35.242E-9 s = 32.141E-6 s
可以计算出水平扫描率大约是31kHz:
1/(32.141E-6 s) = 31.113E3 Hz
完整的屏幕有480线,但是垂直回扫和垂直同步也需要时间,假设垂直回扫和垂
直同步需要49个象素时钟,因此,画一个完整的屏幕的时间是:
(480+49)*32.141E-6 s = 17.002E-3 s
可以计算出垂直扫描率大约是59kHz:
1/(17.002E-3 s) = 58.815 Hz
这意味着屏幕数据每秒钟大约刷新59次。
18.6.3 颜色位域
FBI可变参数var中的red、green和blue位域的设置直接由显示缓冲区与显示点
的对应关系决定,例如,对于RGB565模式,查表18.4,red占据5位,偏移为11位;
green占据6位,偏移为5位;blue占据5位,偏移为0位,即:
fbinfo->var.red.offset = 11;
fbinfo->var.green.offset = 5;
fbinfo->var.blue.offset = 0;
fbinfo->var.transp.offset = 0;
fbinfo->var.red.length = 5;
fbinfo->var.green.length = 6;
fbinfo->var.blue.length = 5;
18.6.4 固定参数
FBI固定参数fix中的smem_start指示帧缓冲设备显示缓冲区的首地址,
smem_len
为帧缓冲设备显示缓冲区的大小,计算公式为:
smem_len = max_xres * max_yres * max_bpp
即:帧缓冲设备显示缓冲区的大小 = 最大的x解析度 * 最大的y解析度 * 最大
的BPP。
帧缓冲设备驱动的fb_ops成员函数
FBI中的fp_ops是使得帧缓冲设备工作所需函数的集合,它们最终与LCD控制器
硬件打交道。
fb_check_var()用于调整可变参数,并修正为硬件所支持的值;fb_set_par()则根据
屏幕参数设置具体读写LCD控制器的寄存器以使得LCD控制器进入相应的工作状
态。
对于fb_ops中的fb_fillrect()、fb_copyarea()和fb_imageblit()成员函数,通常直接
使用对应的通用的cfb_fillrect()、cfb_copyarea()和cfb_imageblit()函数即可。cfb_fillrect()
函数定义在 drivers/video/cfbfillrect.c 文件中,cfb_copyarea() 定义在
drivers/video/cfbcopyarea.c 文件中,cfb_imageblit()定义在drivers/video/cfbimgblt.c 文件
中。
fb_ops 中的fb_setcolreg() 成员函数实现伪颜色表(针对
FB_VISUAL_TRUECOLOR、FB_ VISUAL_DIRECTCOLOR模式)和颜色表的填充,
其模板如代码清单18.11所示。
代码清单18.11 fb_setcolreg()函数模板
1 static int xxxfb_setcolreg(unsigned regno, unsigned red, unsigned
green,
2 unsigned blue, unsigned transp, struct fb_info *info)
3 {
4 struct xxxfb_info *fbi = info->par;
5 unsigned int val;
6
7 switch (fbi->fb->fix.visual)
8 {
9 case FB_VISUAL_TRUECOLOR:
10 /* 真彩色,设置伪颜色表 */
11 if (regno < 16)
12 {
13 u32 *pal = fbi->fb->pseudo_palette;
14
15 val = chan_to_field(red, &fbi->fb->var.red);
16 val |= chan_to_field(green, &fbi->fb->var.green);
17 val |= chan_to_field(blue, &fbi->fb->var.blue);
18
19 pal[regno] = val;
20 }
21 break;
22
23 case FB_VISUAL_PSEUDOCOLOR:
24 if (regno < 256)
25 {
26 /* RGB565模式 */
27 val = ((red >> 0) &0xf800);
28 val |= ((green >> 5) &0x07e0);
29 val |= ((blue >> 11) &0x001f);
30
31 writel(val, XXX_TFTPAL(regno));
32 schedule_palette_update(fbi, regno, val);
33 }
34 break;
35 ...
36 }
37
38 return 0;
39 }
LCD设备驱动的读写、mmap和ioctl 函数
虽然帧缓冲设备的
file_operations 中的成员函数
,即文件操作函数已经由内核在
fbmem.c 文件中实现,一般不再需要驱动工程师修改,但分析这些函数对于巩固字符
设备驱动的知识以及加深对帧缓冲设备驱动的理解是大有裨益的。
代码清单18.12所示为LCD设备驱动的文件操作读写函数的源代码,从代码结构
及习惯而言,与本书第二篇所讲解的字符设备驱动完全一致。
代码清单18.12 帧缓冲设备驱动的读写函数
1 static ssize_t fb_read(struct file *file, char __user *buf, size_t count,
2 loff_t *ppos)
3 {
4 unsigned long p = *ppos;
5 struct inode *inode = file->f_dentry->d_inode;
6 int fbidx = iminor(inode);
7 struct fb_info *info = registered_fb[fbidx]; //获得fb_info
8 u32 *buffer, *dst;
9 u32 __iomem *src;
10 int c, i, cnt = 0, err = 0;
11 unsigned long total_size;
12
13 if (!info || !info->screen_base)
14 return - ENODEV;
15
16 if (info->state != FBINFO_STATE_RUNNING)
17 return - EPERM;
18
19 if (info->fbops->fb_read) //如果fb_ops中定义了特定的读函数 ,读缓冲区的函数
20 return info->fbops->fb_read(file, buf, count, ppos);
21 /*获得显示缓冲区总的大小*/
22 total_size = info->screen_size;
23
24 if (total_size == 0)
25 total_size = info->fix.smem_len;
26
27 if (p >= total_size)
28 return 0;
29 /*获得有效的读长度*/
30 if (count >= total_size)
31 count = total_size;
32
33 if (count + p > total_size)
34 count = total_size - p;
35 /*分配用于临时存放显示缓冲区数据的buffer*/
36 buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count GFP_KERNEL);
37 if (!buffer)
38 return - ENOMEM;
39
40 src = (u32 _ _iomem*)(info->screen_base + p); //获得源地址
41
42 if (info->fbops->fb_sync)
43 info->fbops->fb_sync(info);
44
45 while (count)
46 { /*读取显示缓冲区中的数据并复制到分配的buffer*/
47 c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
48 dst = buffer;
49 for (i = c >> 2; i--;)
50 *dst++ = fb_readl(src++);
51 if (c &3)
52 {
53 u8 *dst8 = (u8*)dst;
54 u8 _ _iomem *src8 = (u8 _ _iomem*)src;
55
56 for (i = c &3; i--;)
57 *dst8++ = fb_readb(src8++);
58
59 src = (u32 _ _iomem*)src8;
60 }
61
62 if (copy_to_user(buf, buffer, c)) //复制到用户空间
63 {
64 err = - EFAULT;
65 break;
66 }
67 *ppos += c;
68 buf += c;
69 cnt += c;
70 count -= c;
71 }
72
73 kfree(buffer);
74
75 return (err) ? err : cnt;
76 }
77
78 static ssize_t fb_write(struct file *file, const char _ _user *buf, size_t count,
79 loff_t *ppos)
80 {
81 unsigned long p = *ppos;
82 struct inode *inode = file->f_dentry->d_inode;
83 int fbidx = iminor(inode);
84 struct fb_info *info = registered_fb[fbidx];
85 u32 *buffer, *src;
86 u32 _ _iomem *dst;
87 int c, i, cnt = 0, err = 0;
88 unsigned long total_size;
89
90 if (!info || !info->screen_base)
91 return - ENODEV;
92
93 if (info->state != FBINFO_STATE_RUNNING)
94 return - EPERM;
95
96 if (info->fbops->fb_write) //如果fb_ops中定义了特定的写函数 (即:s3cfb.c中)
97 return info->fbops->fb_write(file, buf, count, ppos);
98 /*获得显示缓冲区总的大小*/
99 total_size = info->screen_size;
100
101 if (total_size == 0)
102 total_size = info->fix.smem_len;
103
104 if (p > total_size)
105 return 0;
106 /*获得有效的写长度*/
107 if (count >= total_size)
108 count = total_size;
109
110 if (count + p > total_size)
111 count = total_size - p;
112 /*分配用于存放用户空间传过来的显示缓冲区数据的buffer*/
113 buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count, GFP_KERNEL);
114 if (!buffer)
115 return - ENOMEM;
116
117 dst = (u32 _ _iomem*)(info->screen_base + p); //要写的显示缓冲区基地址
118
119 if (info->fbops->fb_sync)
120 info->fbops->fb_sync(info);
121
122 while (count)
123 { /*读取用户空间数据并复制到显示缓冲区*/
124 c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
125 src = buffer;
126
127 if (copy_from_user(src, buf, c))
128 {
129 err = - EFAULT;
130 break;
131 }
132
133 for (i = c >> 2; i--;)
134 fb_writel(*src++, dst++);
135
136 if (c &3)
137 {
138 u8 *src8 = (u8*)src;
139 u8 _ _iomem *dst8 = (u8 _ _iomem*)dst;
140
141 for (i = c &3; i--;)
142 fb_writeb(*src8++, dst8++);
143
144 dst = (u32 _ _iomem*)dst8;
145 }
146
147 *ppos += c;
148 buf += c;
149 cnt += c;
150 count -= c;
151 }
152
153 kfree(buffer);
154
155 return (err) ? err : cnt;
156 }
file_operations中的mmap()函数非常关键,它将显示缓冲区映射到用户空间,从
而使得用户空间可以直接操作显示缓冲区而省去一次用户空间到内核空间的内存复
制过程,提高效率,其源代码如代码清单18.13所示。
代码清单18.13 帧缓冲设备驱动的mmap函数
1 static int fb_mmap(struct file *file, struct vm_area_struct *vma)
2 {
3 int fbidx = iminor(file->f_dentry->d_inode);
4 struct fb_info *info = registered_fb[fbidx];
5 struct fb_ops *fb = info->fbops;
6 unsigned long off;
7
8 if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
9 return - EINVAL;
10 off = vma->vm_pgoff << PAGE_SHIFT;
11 if (!fb)
12 return - ENODEV;
13 if (fb->fb_mmap) // fb_info 中实现了mmap,则调用fb_info 的mmap
14 {
15 int res;
16 lock_kernel();
17 res = fb->fb_mmap(info, vma);
18 unlock_kernel();
19 return res;
20 }
21
22 /* !sparc32... */
23 lock_kernel();
24
25 /* 映射帧缓冲设备的显示缓冲区 */
26 start = info->fix.smem_start; //开始地址
27 len = PAGE_ALIGN((start &~PAGE_MASK) + info->fix.smem_len); //长度
28 if (off >= len)
29 {
30 /* 内存映射的I/O */
31 off -= len;
32 if (info->var.accel_flags)
33 {
34 unlock_kernel();
35 return - EINVAL;
36 }
37 start = info->fix.mmio_start;
38 len = PAGE_ALIGN((start &~PAGE_MASK) + info->fix.mmio_len);
39 }
40 unlock_kernel();
41 start &= PAGE_MASK;
42 if ((vma->vm_end - vma->vm_start + off) > len)
43 return - EINVAL;
44 off += start;
45 vma->vm_pgoff = off >> PAGE_SHIFT;
46 /* 这是1个 I/O映射 */
47 vma->vm_flags |= VM_I/O | VM_RESERVED;
48 vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
49
50 if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
51 vma->vm_end -vma->vm_start, vma->vm_page_prot)) //映射
52 return - EAGAIN;
53
54 return 0;
55 }
fb_ioctl() 函数最终实现对用户I/O 控制命令的执行,这些命令包括
FBIOGET_VSCREENINFO(获得可变的屏幕参数)、FBIOPUT_VSCREENINFO(设置可
变的屏幕参数)、FBIOGET _FSCREENINFO(获得固定的屏幕参数设置,注意,固定
的屏幕参数不能由用户设置)、FBIOPUTCMAP(设置颜色表)、FBIOGETCMAP
(获得颜色表)等。代码清单18.14所示为帧缓冲设备ioctl()函数的源代码。
代码清单18.14 帧缓冲设备驱动的ioctl函数
1 static int fb_ioctl(struct inode *inode, struct file *file, unsigned
int cmd,
2 unsigned long arg)
3 {
4 int fbidx = iminor(inode);
5 struct fb_info *info = registered_fb[fbidx];
6 struct fb_ops *fb = info->fbops;
7 struct fb_var_screeninfo var;
8 struct fb_fix_screeninfo fix;
9 struct fb_con2fbmap con2fb;
10 struct fb_cmap_user cmap;
11 struct fb_event event;
12 void _ _user *argp = (void _ _user*)arg;
13 int i;
14
15 if (!fb)
16 return - ENODEV;
17 switch (cmd)
18 {
19 case FBIOGET_VSCREENINFO: // 获得 可变的屏幕参数
20 return copy_to_user(argp, &info->var, sizeof(var)) ? - EFAULT: 0;
21 case FBIOPUT_VSCREENINFO: //设置 可变的屏幕参数
22 if (copy_from_user(&var, argp, sizeof(var)))
23 return - EFAULT;
24 acquire_console_sem();
25 info->flags |= FBINFO_MISC_USEREVENT;
26 i = fb_set_var(info, &var);
27 info->flags &= ~FBINFO_MISC_USEREVENT;
28 release_console_sem();
29 if (i)
30 return i;
31 if (copy_to_user(argp, &var, sizeof(var)))
32 return - EFAULT;
33 return 0;
34 case FBIOGET_FSCREENINFO: //获得 固定的屏幕参数设置
35 return copy_to_user(argp, &info->fix, sizeof(fix)) ? - EFAULT: 0;
36 case FBIOPUTCMAP: //设置颜色表
37 if (copy_from_user(&cmap, argp, sizeof(cmap)))
38 return - EFAULT;
39 return (fb_set_user_cmap(&cmap, info));
40 case FBIOGETCMAP: //获得颜色表
41 if (copy_from_user(&cmap, argp, sizeof(cmap)))
42 return - EFAULT;
43 return fb_cmap_to_user(&info->cmap, &cmap);
44 case FBIOPAN_DISPLAY:
45 if (copy_from_user(&var, argp, sizeof(var)))
46 return - EFAULT;
47 acquire_console_sem();
48 i = fb_pan_display(info, &var);
49 release_console_sem();
50 if (i)
51 return i;
52 if (copy_to_user(argp, &var, sizeof(var)))
53 return - EFAULT;
54 return 0;
55 case FBIO_CURSOR:
56 return - EINVAL;
57 case FBIOGET_CON2FBMAP:
58 if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
59 return - EFAULT;
60 if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
61 return - EINVAL;
62 con2fb.framebuffer = - 1;
63 event.info = info;
64 event.data = &con2fb;
65 notifier_call_chain(&fb_notifier_list, FB_EVENT_GET_CONSOLE_MAP, &event);
66 return copy_to_user(argp, &con2fb, sizeof(con2fb)) ? - EFAULT: 0;
67 case FBIOPUT_CON2FBMAP:
68 if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
69 return - EFAULT;
70 if (con2fb.console < 0 || con2fb.console > MAX_NR_CONSOLES)
71 return - EINVAL;
72 if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX)
73 return - EINVAL;
74 #ifdef CONFIG_KMOD
75 if (!registered_fb[con2fb.framebuffer])
76 try_to_load(con2fb.framebuffer);
77 #endif /* CONFIG_KMOD */
78 if (!registered_fb[con2fb.framebuffer])
79 return - EINVAL;
80 event.info = info;
81 event.data = &con2fb;
82 return notifier_call_chain(&fb_notifier_list,
83 FB_EVENT_SET_CONSOLE_MAP, &event);
84 case FBIOBLANK:
85 acquire_console_sem();
86 info->flags |= FBINFO_MISC_USEREVENT;
87 i = fb_blank(info, arg);
88 info->flags &= ~FBINFO_MISC_USEREVENT;
89 release_console_sem();
90 return i;
91 default:
92 if (fb->fb_ioctl == NULL)
93 return - EINVAL;
94 return fb->fb_ioctl(info, cmd, arg);
95 }
96 }
1
18
18.
18.4
18.4
18.4
18.4