分析linux的lcd驅動
2015/07/22 17:31
瀏覽436
迴響0
推薦0
引用0
一、LCD硬件原理
利用液晶制成的顯示器稱為LCD;依據驅動的方式可分為靜態驅動、簡單矩陣驅動和主動矩陣驅動;
主動式TFT型的液晶顯示器結構較復雜,包括背光管、導光板、偏光板、濾光板、玻璃基板、配向膜、液晶材料和薄膜式晶體管;
在TFT型LCD中,晶體管矩陣依顯示信號開啟或關閉液晶分子的電壓,使液晶分子軸轉向而成“亮”或“暗”的對比;
一塊LCD屏顯示圖像不但需要LCD驅動器,還需要有相應的LCD控制器。通常LCD驅動器會以COF/COG的形式與LCD玻璃基板制作在一起,
而LCD控制器側由外部電路來實現。通過LCD控制器可以方便的控制STN和TFT屏。
TFT屏的一些信號:
VCLK:像素時鐘信號;
HSYNC:行同步信號;
VSYNC:幀同步信號;
VDEN:數據有效標識信號;
VD:圖像的數據信號;
二、幀緩沖的概念
幀緩沖是LINUX系統為顯示設備提供的一個接口,它將顯示緩沖區抽象,屏蔽圖像硬件的底層差異,允許上層應用程序在圖形模式下
直接對顯示緩沖區進行讀寫操作,用戶不必關心物理顯示緩沖區的具體位置及存放方式,這些都由幀緩沖設備驅動本身來完成。對於
幀緩沖設備而言,只要在顯示緩沖區中與顯示點對應的區域寫入顏色值,對應的顏色會自動在屏幕上顯示。
幀緩沖設備為標準的字符設備,主設備號為29,對應/dev/fbn設備文件。
三、幀緩沖相關數據結構
1、linux中使用fb_info結構描述幀緩沖,fb_info中描述了幀緩沖設備的屬性和操作,每一個幀緩沖設備都對應一個fb_info結構:
//定義在include/linux/fb.h中
struct fb_info {
atomic_t count;//引用計數
int node;
int flags;
struct mutex lock; //用於open/release/ioctl函數的鎖
struct mutex mm_lock; /* fb_mmap和smem_*的鎖*/
struct fb_var_screeninfo var; //可變參數
struct fb_fix_screeninfo fix; //固定參數
struct fb_monspecs monspecs; //顯示器模式
struct work_struct queue; //幀緩沖事件的隊列
struct fb_pixmap pixmap; //圖像硬件映射器
struct fb_pixmap sprite; //光標硬件映射器
struct fb_cmap cmap; //當前的顏色表
struct list_head modelist; //模式列表
struct fb_videomode *mode; //當前的video模式
#ifdef CONFIG_FB_BACKLIGHT
struct backlight_device *bl_dev;//對應的背光設備
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;//延遲IO
#endif
struct fb_ops *fbops;//幀緩沖操作函數集合
struct device *device; //父設備
struct device *dev; //fb設備
int class_flag; //私有sysfs標誌
#ifdef CONFIG_FB_TILEBLITTING
struct fb_tile_ops *tileops; //圖塊 Blitting
#endif
char __iomem *screen_base; //虛擬基地址
unsigned long screen_size; //ioremapped虛擬內存大小
void *pseudo_palette; //假的16個顏色的調色板
#define FBINFO_STATE_RUNNING 0
#define FBINFO_STATE_SUSPENDED 1
u32 state; //硬件狀態
void *fbcon_par; //私有區域
void *par;
struct apertures_struct {
unsigned int count;
struct aperture {
resource_size_t base;
resource_size_t size;
} ranges[0];
} *apertures;
};
fb_info描述了幀緩沖設備的設置參數、狀態以及操作函數指針;
2、幀緩沖可變參數結構fb_var_screeninfo
struct fb_var_screeninfo {
__u32 xres; //可見解析度x
__u32 yres; //可見解析度y
__u32 xres_virtual; //虛擬解析度x
__u32 yres_virtual; //虛擬解析度y
__u32 xoffset; //虛擬到可見之間的偏移x
__u32 yoffset; //虛擬到可見之間的偏移y
__u32 bits_per_pixel; //每像素位數BPP
__u32 grayscale; //非0時指灰度
//fb緩存的R、G、B位域
struct fb_bitfield red; //紅
struct fb_bitfield green; //綠
struct fb_bitfield blue; //藍
struct fb_bitfield transp; //透明度
__u32 nonstd; //非0時指非標準像素格式
__u32 activate; /* see FB_ACTIVATE_* */
__u32 height; //高度
__u32 width; //寬度
__u32 accel_flags; /* (OBSOLETE) see fb_info.flags */
__u32 pixclock; //像素時鐘(皮秒)
__u32 left_margin; //行切換,從同步到繪圖之間的延遲
__u32 right_margin; //行切換,從繪圖到同步之間的延遲
__u32 upper_margin; //幀切換,從同步到繪圖之間的延遲
__u32 lower_margin;//幀切換,從繪圖到同步之間的延遲
__u32 hsync_len; //水平同步的長度
__u32 vsync_len; //垂直同步的長度
__u32 sync; /* see FB_SYNC_* */
__u32 vmode; /* see FB_VMODE_* */
__u32 rotate; //反時針旋轉角
__u32 reserved[5]; //留給未來的兼容性
};
3、描述每一像素顯示緩沖區的組織方式結構fb_bitfield
struct fb_bitfield {
__u32 offset; //位域偏移
__u32 length; //位域長度
__u32 msb_right; //MSB(最高有效位)指示
};
4、幀緩沖設備固定參數結構fb_fix_screeninfo
struct fb_fix_screeninfo {
char id[16]; //字符串形式標示符
unsigned long smem_start;//fb緩沖內存的開始地址(物理地址)
__u32 smem_len; //fb緩沖長度
__u32 type; /* see FB_TYPE_* */
__u32 type_aux; /* Interleave for interleaved Planes */
__u32 visual; /* see FB_VISUAL_* */
__u16 xpanstep; /* 硬件平移x */
__u16 ypanstep; /* 硬件平移y */
__u16 ywrapstep; /* zero if no hardware ywrap */
__u32 line_length; /* 行長度 */
unsigned long mmio_start; //內存映射IO的開始地址(物理地址)
__u32 mmio_len; //內存映射IO的長度
__u32 accel;
__u16 reserved[3]; //保留
};
5、幀緩沖設備文件操作函數集合fb_ops
struct fb_ops {
struct module *owner;//模塊所屬
int (*fb_open)(struct fb_info *info, int user);//打開
int (*fb_release)(struct fb_info *info, int user);//釋放
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);
int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);//檢查可變參數
int (*fb_set_par)(struct fb_info *info);//設置video模式
int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,//設置color寄存器
unsigned blue, unsigned transp, struct fb_info *info);
int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);//設置顏色表
int (*fb_blank)(int blank, struct fb_info *info);//顯示空白
int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);//pan顯示
void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);//矩形填充
void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);//數據復制
void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);//圖像填充
int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);//繪制光標
void (*fb_rotate)(struct fb_info *info, int angle);//旋轉顯示
int (*fb_sync)(struct fb_info *info);//等待blit空閑
int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,//IO控制
unsigned long arg);
int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,//處理32位IO控制
unsigned long arg);
int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);//fb特定的mmap
void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,
struct fb_var_screeninfo *var);//根據var得到顯示器的功能信息
void (*fb_destroy)(struct fb_info *info);//銷毀fb
int (*fb_debug_enter)(struct fb_info *info);
int (*fb_debug_leave)(struct fb_info *info);
};
四、幀緩沖子系統初始化過程
在driver/video/fbmem.c中實現了幀緩沖相關的大部分功能
1、幀緩沖子系統初始化
static int __init fbmem_init(void)
{
//創建proc數據
proc_create("fb", 0, NULL, &fb_proc_fops);
//創建並註冊一些以主設備號為29,次設備號為0到256,設備名為fbN的字符設備
if (register_chrdev(FB_MAJOR,"fb",&fb_fops))
printk("unable to get major %d for fb devs\n", FB_MAJOR);
//在sys下創建graphics類
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;
}
__init fbmem_init函數的功能為:
1)創建proc數據
2)創建並註冊一些以主設備號為29,次設備號為0到256,設備名為fbN的字符設備
3)在sys下創建graphics類
//創建並註冊一些以主設備號為29,次設備號為0到256,設備名為fbN的字符設備
static inline int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)
{ //註冊此設備號為0到256的字符設備
return __register_chrdev(major, 0, 256, name, fops);
}
//註冊此設備號為0到256的字符設備
int __register_chrdev(unsigned int major, unsigned int baseminor,
unsigned int count, const char *name,
const struct file_operations *fops)
{
struct char_device_struct *cd;
struct cdev *cdev;
int err = -ENOMEM;
//申請設備號
cd = __register_chrdev_region(major, baseminor, count, name);
if (IS_ERR(cd))
return PTR_ERR(cd);
//分配cdev結構體
cdev = cdev_alloc();
if (!cdev)
goto out2;
//為cdev結構體賦值
cdev->owner = fops->owner;
cdev->ops = fops;
kobject_set_name(&cdev->kobj, "%s", name);
//向內核添加cdev結構
err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);
if (err)
goto out;
cd->cdev = cdev;
return major ? 0 : cd->major;
out:
kobject_put(&cdev->kobj);
out2:
kfree(__unregister_chrdev_region(cd->major, baseminor, count));
return err;
}
2、幀緩沖設備打開方法
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;
//獲得設備文件對應的幀緩沖設備結構,所有註冊的幀緩沖設備fb_info都被保存在registered_fb[FB_MAX]中,最多32個
info = get_fb_info(fbidx);
if (!info) {
//加載幀緩沖設備對應的模塊
request_module("fb%d", fbidx);
//確認模塊是否加載成功
info = get_fb_info(fbidx);
if (!info)
return -ENODEV;
}
if (IS_ERR(info))
return PTR_ERR(info);
mutex_lock(&info->lock);
//嘗試獲得info->fbops所在的模塊
if (!try_module_get(info->fbops->owner)) {
res = -ENODEV;
goto out;
}
//將幀緩沖設備結構作為設備文件的私有數據保存,用於在其它方法中傳遞
file->private_data = info;
if (info->fbops->fb_open) {
//若幀緩沖設備結構中定義了open方法,則調用對應的open方法
res = info->fbops->fb_open(info,1);
if (res)
module_put(info->fbops->owner);
}
#ifdef CONFIG_FB_DEFERRED_IO
if (info->fbdefio)
//若定義了延遲IO操作,調用對應的延遲IO打開函數
fb_deferred_io_open(info, inode, file);
#endif
out:
mutex_unlock(&info->lock);
if (res)
put_fb_info(info);
return res;
}
fb_open函數的功能為:
1)獲得打開的設備文件對應的幀緩沖設備的索引
2)獲得設備文件對應的幀緩沖設備結構
3)加載幀緩沖設備對應的模塊
4)將幀緩沖設備結構作為設備文件的私有數據保存
5)若幀緩沖設備結構中定義了open方法,則調用對應的open方法
6)若定義了延遲IO操作,調用對應的延遲IO打開函數
get_fb_info函數
static struct fb_info *get_fb_info(unsigned int idx)
{
struct fb_info *fb_info;
//幀緩沖設備最多為FB_MAX=32個
if (idx >= FB_MAX)
return ERR_PTR(-ENODEV);
mutex_lock(®istration_lock);
//根據索引從所有註冊的fb_info中找到對應的fb_info結構
fb_info = registered_fb[idx];
if (fb_info)
//增加fb_info的引用次數
atomic_inc(&fb_info->count);
mutex_unlock(®istration_lock);
return fb_info;
}
get_fb_info函數功能為:
1)根據索引從所有註冊的fb_info中找到對應的fb_info結構
2)增加fb_info的引用次數
//保存所有註冊的幀緩沖設備,最多32個,在fb_info註冊方法中將所有註冊的fb_info都保存在registered_fb數組中
struct fb_info *registered_fb[FB_MAX];
3、幀緩沖設備讀方法
static ssize_t fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
unsigned long p = *ppos;
struct fb_info *info = file_fb_info(file);//獲得設備文件對應的幀緩沖設備結構
u8 *buffer, *dst;
u8 __iomem *src;
int c, cnt = 0, err = 0;
unsigned long total_size;
//判斷幀緩沖設備是否為空,虛擬基地址是否為空
if (!info || ! info->screen_base)
return -ENODEV;
//判斷幀緩沖設備是否在運行
if (info->state != FBINFO_STATE_RUNNING)
return -EPERM;
//若幀緩沖設備定義了fb_read方法,調用對應的read方法
if (info->fbops->fb_read)
return info->fbops->fb_read(info, buf, count, ppos);
//獲得ioremapped虛擬內存大小
total_size = info->screen_size;
if (total_size == 0)
total_size = info->fix.smem_len;
if (p >= total_size)
return 0;
if (count >= total_size)
count = total_size;
if (count + p > total_size)
count = total_size - p;
//分配緩沖區
buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
GFP_KERNEL);
if (!buffer)
return -ENOMEM;
//獲得需要操作的虛擬基地址
src = (u8 __iomem *) (info->screen_base + p);
//調用等待blit空閑方法
if (info->fbops->fb_sync)
info->fbops->fb_sync(info);
while (count) {
c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
dst = buffer;
//從物理顯示緩沖區讀取數據
fb_memcpy_fromfb(dst, src, c);
dst += c;
src += c;
//復制數據到用戶空間
if (copy_to_user(buf, buffer, c)) {
err = -EFAULT;
break;
}
*ppos += c;
buf += c;
cnt += c;
count -= c;
}
kfree(buffer);
return (err) ? err : cnt;
}
fb_read函數功能為:
1)獲得設備文件對應的幀緩沖設備結構
2)判斷幀緩沖設備是否為空,虛擬基地址是否為空;判斷幀緩沖設備是否在運行,
若幀緩沖設備定義了fb_read方法,調用對應的read方法
3)若未定義fb_read方法,獲得ioremapped虛擬內存大小,分配緩沖區
4)獲得需要操作的虛擬基地址,調用等待blit空閑方法
5)從物理顯示緩沖區讀取數據,復制數據到用戶空間
file_fb_info函數
static struct fb_info *file_fb_info(struct file *file)
{
struct inode *inode = file->f_path.dentry->d_inode;//獲得設備節點
int fbidx = iminor(inode);//根據設備節點獲得fb_info結構索引
struct fb_info *info = registered_fb[fbidx];//根據索引從所有註冊的fb_info中找到對應的fb_info結構
if (info != file->private_data)
info = NULL;
return info;
}
file_fb_info函數的功能為根據設備文件獲得設備節點,根據設備節點獲得fb_info結構索引,根據索引從所有註冊的
fb_info中找到對應的fb_info結構
fb_memcpy_fromfb函數
#define fb_memcpy_fromfb memcpy_fromio
void memcpy_fromio(void *to, const volatile void __iomem *from, long count)
{
char *dst = to;
while (count) {
count--;
*dst++ = readb(from++);//讀取IO內存
}
}
EXPORT_SYMBOL(memcpy_fromio);
memcpy_fromio函數的功能為從IO空間讀取數據到內存
4、幀緩沖設備寫方法
static ssize_t fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
unsigned long p = *ppos;
struct fb_info *info = file_fb_info(file);//獲得設備文件對應的幀緩沖設備結構
u8 *buffer, *src;
u8 __iomem *dst;
int c, cnt = 0, err = 0;
unsigned long total_size;
//判斷幀緩沖設備是否為空,虛擬基地址是否為空
if (!info || !info->screen_base)
return -ENODEV;
//判斷幀緩沖設備是否在運行
if (info->state != FBINFO_STATE_RUNNING)
return -EPERM;
//若幀緩沖設備定義了fb_write方法,調用對應的write方法
if (info->fbops->fb_write)
return info->fbops->fb_write(info, buf, count, ppos);
//獲得ioremapped虛擬內存大小
total_size = info->screen_size;
if (total_size == 0)
total_size = info->fix.smem_len;
if (p > total_size)
return -EFBIG;
if (count > total_size) {
err = -EFBIG;
count = total_size;
}
if (count + p > total_size) {
if (!err)
err = -ENOSPC;
count = total_size - p;
}
//分配緩沖區
buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
GFP_KERNEL);
if (!buffer)
return -ENOMEM;
//獲得需要操作的虛擬基地址
dst = (u8 __iomem *) (info->screen_base + p);
//調用等待blit空閑方法
if (info->fbops->fb_sync)
info->fbops->fb_sync(info);
while (count) {
c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
src = buffer;
//從用戶空間復制數據到分配的緩沖區
if (copy_from_user(src, buf, c)) {
err = -EFAULT;
break;
}
//復制緩沖區的內容到物理顯示緩沖區中
fb_memcpy_tofb(dst, src, c);
dst += c;
src += c;
*ppos += c;
buf += c;
cnt += c;
count -= c;
}
kfree(buffer);
return (cnt) ? cnt : err;
}
fb_write函數功能為:
1)獲得設備文件對應的幀緩沖設備結構
2)判斷幀緩沖設備是否為空,虛擬基地址是否為空;判斷幀緩沖設備是否在運行,
若幀緩沖設備定義了fb_write方法,調用對應的write方法
3)若未定義fb_write方法,獲得ioremapped虛擬內存大小,分配緩沖區
4)獲得需要操作的虛擬基地址,調用等待blit空閑方法
5)從用戶空間復制數據到分配的緩沖區,復制緩沖區的內容到物理顯示緩沖區中
fb_memcpy_tofb函數
#define fb_memcpy_tofb memcpy_toio
void memcpy_toio(volatile void __iomem *to, const void *from, long count)
{
const char *src = from;
while (count) {
count--;
writeb(*src++, to++);//寫入IO內存
}
}
EXPORT_SYMBOL(memcpy_toio);
memcpy_toio函數的功能為將內存空間的數據寫入到IO空間
5、幀緩沖設備內存映射方法
static int fb_mmap(struct file *file, struct vm_area_struct * vma)
{
struct fb_info *info = file_fb_info(file);//獲得設備文件對應的幀緩沖設備結構
struct fb_ops *fb;
unsigned long off;
unsigned long start;
u32 len;
if (!info)
return -ENODEV;
if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
return -EINVAL;
off = vma->vm_pgoff << PAGE_SHIFT;
fb = info->fbops;//獲得fb_info的操作函數集合
if (!fb)
return -ENODEV;
mutex_lock(&info->mm_lock);
if (fb->fb_mmap) {//若fb_info中定義了fb_mmap方法
int res;
//調用fb_info中的fb_mmap方法
res = fb->fb_mmap(info, vma);
mutex_unlock(&info->mm_lock);
return res;
}
/* frame buffer memory */
start = info->fix.smem_start;
len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);
if (off >= len) {
/* memory mapped io */
off -= len;
if (info->var.accel_flags) {
mutex_unlock(&info->mm_lock);
return -EINVAL;
}
start = info->fix.mmio_start;
len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);
}
mutex_unlock(&info->mm_lock);
start &= PAGE_MASK;
if ((vma->vm_end - vma->vm_start + off) > len)
return -EINVAL;
off += start;
vma->vm_pgoff = off >> PAGE_SHIFT;
/* This is an IO map - tell maydump to skip this VMA */
vma->vm_flags |= VM_IO | VM_RESERVED;
vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
fb_pgprotect(file, vma, off);
if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
vma->vm_end - vma->vm_start, vma->vm_page_prot))
return -EAGAIN;
return 0;
}
五、幀緩沖設備註冊
int register_framebuffer(struct fb_info *fb_info)
{
int ret;
mutex_lock(®istration_lock);
//調用do_register_framebuffer函數註冊幀緩沖設備
ret = do_register_framebuffer(fb_info);
mutex_unlock(®istration_lock);
return ret;
}
register_framebuffer函數調用do_register_framebuffer方法註冊幀緩沖設備
do_register_framebuffer函數
static int do_register_framebuffer(struct fb_info *fb_info)
{
int i;
struct fb_event event;
struct fb_videomode mode;
//檢測幀緩沖設備的標誌位
if (fb_check_foreignness(fb_info))
return -ENOSYS;
//註銷有沖突的幀緩沖設備
do_remove_conflicting_framebuffers(fb_info->apertures, fb_info->fix.id,
fb_is_primary_device(fb_info));
if (num_registered_fb == FB_MAX)
return -ENXIO;
num_registered_fb++;//全局變量,記錄註冊的幀緩沖設備數
for (i = 0 ; i < FB_MAX; i++)
if (!registered_fb[i])
break;
fb_info->node = i;//設置設備節點
atomic_set(&fb_info->count, 1);//設備引用計數為1
mutex_init(&fb_info->lock);
mutex_init(&fb_info->mm_lock);
//在/dev中創建設備文件
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
//初始化dev設備文件
fb_init_device(fb_info);
if (fb_info->pixmap.addr == NULL) {
//分配圖形映射區域
fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);
//為fb_iinfo結構的圖形映射區域賦值
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);
//將幀緩沖設備的可變參數轉化為video模式
fb_var_to_videomode(&mode, &fb_info->var);
//添加video模式入口到mode列表中
fb_add_videomode(&mode, &fb_info->modelist);
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;
}
do_register_framebuffer函數的功能為
1)檢測幀緩沖設備的標誌位
2)註銷有沖突的幀緩沖設備
3)增加全局幀緩沖設備數;設置註冊的幀緩沖設備節點,設備引用計數為1
4)在/dev中創建設備文件,初始化dev設備文件
5)分配圖形映射區域,為fb_iinfo結構的圖形映射區域賦值
6)將幀緩沖設備的可變參數轉化為video模式,添加video模式入口到mode列表中
7)將註冊的幀緩沖設備添加全局幀緩沖設備數組中,註冊客戶端通知
fb_check_foreignness函數功能為檢測fb_info的標誌位
static int fb_check_foreignness(struct fb_info *fi)
{
const bool foreign_endian = fi->flags & FBINFO_FOREIGN_ENDIAN;
fi->flags &= ~FBINFO_FOREIGN_ENDIAN;
#ifdef __BIG_ENDIAN
fi->flags |= foreign_endian ? 0 : FBINFO_BE_MATH;
#else
fi->flags |= foreign_endian ? FBINFO_BE_MATH : 0;
#endif /* __BIG_ENDIAN */
if (fi->flags & FBINFO_BE_MATH && !fb_be_math(fi)) {
pr_err("%s: enable CONFIG_FB_BIG_ENDIAN to "
"support this framebuffer\n", fi->fix.id);
return -ENOSYS;
} else if (!(fi->flags & FBINFO_BE_MATH) && fb_be_math(fi)) {
pr_err("%s: enable CONFIG_FB_LITTLE_ENDIAN to "
"support this framebuffer\n", fi->fix.id);
return -ENOSYS;
}
return 0;
}
do_remove_conflicting_framebuffers函數的功能為註銷有沖突的幀緩沖設備
static void do_remove_conflicting_framebuffers(struct apertures_struct *a,
const char *name, bool primary)
{
int i;
/* check all firmware fbs and kick off if the base addr overlaps */
//檢測所有幀緩沖固件,去除基地址重疊的幀緩沖設備
for (i = 0 ; i < FB_MAX; i++) {
struct apertures_struct *gen_aper;
if (!registered_fb[i])
continue;
if (!(registered_fb[i]->flags & FBINFO_MISC_FIRMWARE))
continue;
gen_aper = registered_fb[i]->apertures;
if (fb_do_apertures_overlap(gen_aper, a) ||
(primary && gen_aper && gen_aper->count &&
gen_aper->ranges[0].base == VGA_FB_PHYS)) {
printk(KERN_INFO "fb: conflicting fb hw usage "
"%s vs %s - removing generic driver\n",
name, registered_fb[i]->fix.id);
do_unregister_framebuffer(registered_fb[i]);
}
}
}
六、總結
通過以上分析可知,幀緩沖設備實質是字符設備,內核抽象了一個fb_info結構用來描述幀緩沖設備,通過registered_fb數組
保存所有註冊的fb_info結構,num_registered_fb表示註冊的fb_info結構數。內核實現了字符設備對用戶空間的接口,當調用
接口函數時,內核會判斷對應的fb_info是否實現了對應的方法,若實現了,就會調用fb_info結構中定義的方法,若未實現,
則進行內核的默認處理。
因此實現幀緩沖設備的過程是,分配fb_info結構,填充fb_info結構成員,向內核註冊fb_info結構。其他功能內核代碼已經實現
利用液晶制成的顯示器稱為LCD;依據驅動的方式可分為靜態驅動、簡單矩陣驅動和主動矩陣驅動;
主動式TFT型的液晶顯示器結構較復雜,包括背光管、導光板、偏光板、濾光板、玻璃基板、配向膜、液晶材料和薄膜式晶體管;
在TFT型LCD中,晶體管矩陣依顯示信號開啟或關閉液晶分子的電壓,使液晶分子軸轉向而成“亮”或“暗”的對比;
一塊LCD屏顯示圖像不但需要LCD驅動器,還需要有相應的LCD控制器。通常LCD驅動器會以COF/COG的形式與LCD玻璃基板制作在一起,
而LCD控制器側由外部電路來實現。通過LCD控制器可以方便的控制STN和TFT屏。
TFT屏的一些信號:
VCLK:像素時鐘信號;
HSYNC:行同步信號;
VSYNC:幀同步信號;
VDEN:數據有效標識信號;
VD:圖像的數據信號;
二、幀緩沖的概念
幀緩沖是LINUX系統為顯示設備提供的一個接口,它將顯示緩沖區抽象,屏蔽圖像硬件的底層差異,允許上層應用程序在圖形模式下
直接對顯示緩沖區進行讀寫操作,用戶不必關心物理顯示緩沖區的具體位置及存放方式,這些都由幀緩沖設備驅動本身來完成。對於
幀緩沖設備而言,只要在顯示緩沖區中與顯示點對應的區域寫入顏色值,對應的顏色會自動在屏幕上顯示。
幀緩沖設備為標準的字符設備,主設備號為29,對應/dev/fbn設備文件。
三、幀緩沖相關數據結構
1、linux中使用fb_info結構描述幀緩沖,fb_info中描述了幀緩沖設備的屬性和操作,每一個幀緩沖設備都對應一個fb_info結構:
//定義在include/linux/fb.h中
struct fb_info {
atomic_t count;//引用計數
int node;
int flags;
struct mutex lock; //用於open/release/ioctl函數的鎖
struct mutex mm_lock; /* fb_mmap和smem_*的鎖*/
struct fb_var_screeninfo var; //可變參數
struct fb_fix_screeninfo fix; //固定參數
struct fb_monspecs monspecs; //顯示器模式
struct work_struct queue; //幀緩沖事件的隊列
struct fb_pixmap pixmap; //圖像硬件映射器
struct fb_pixmap sprite; //光標硬件映射器
struct fb_cmap cmap; //當前的顏色表
struct list_head modelist; //模式列表
struct fb_videomode *mode; //當前的video模式
#ifdef CONFIG_FB_BACKLIGHT
struct backlight_device *bl_dev;//對應的背光設備
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;//延遲IO
#endif
struct fb_ops *fbops;//幀緩沖操作函數集合
struct device *device; //父設備
struct device *dev; //fb設備
int class_flag; //私有sysfs標誌
#ifdef CONFIG_FB_TILEBLITTING
struct fb_tile_ops *tileops; //圖塊 Blitting
#endif
char __iomem *screen_base; //虛擬基地址
unsigned long screen_size; //ioremapped虛擬內存大小
void *pseudo_palette; //假的16個顏色的調色板
#define FBINFO_STATE_RUNNING 0
#define FBINFO_STATE_SUSPENDED 1
u32 state; //硬件狀態
void *fbcon_par; //私有區域
void *par;
struct apertures_struct {
unsigned int count;
struct aperture {
resource_size_t base;
resource_size_t size;
} ranges[0];
} *apertures;
};
fb_info描述了幀緩沖設備的設置參數、狀態以及操作函數指針;
2、幀緩沖可變參數結構fb_var_screeninfo
struct fb_var_screeninfo {
__u32 xres; //可見解析度x
__u32 yres; //可見解析度y
__u32 xres_virtual; //虛擬解析度x
__u32 yres_virtual; //虛擬解析度y
__u32 xoffset; //虛擬到可見之間的偏移x
__u32 yoffset; //虛擬到可見之間的偏移y
__u32 bits_per_pixel; //每像素位數BPP
__u32 grayscale; //非0時指灰度
//fb緩存的R、G、B位域
struct fb_bitfield red; //紅
struct fb_bitfield green; //綠
struct fb_bitfield blue; //藍
struct fb_bitfield transp; //透明度
__u32 nonstd; //非0時指非標準像素格式
__u32 activate; /* see FB_ACTIVATE_* */
__u32 height; //高度
__u32 width; //寬度
__u32 accel_flags; /* (OBSOLETE) see fb_info.flags */
__u32 pixclock; //像素時鐘(皮秒)
__u32 left_margin; //行切換,從同步到繪圖之間的延遲
__u32 right_margin; //行切換,從繪圖到同步之間的延遲
__u32 upper_margin; //幀切換,從同步到繪圖之間的延遲
__u32 lower_margin;//幀切換,從繪圖到同步之間的延遲
__u32 hsync_len; //水平同步的長度
__u32 vsync_len; //垂直同步的長度
__u32 sync; /* see FB_SYNC_* */
__u32 vmode; /* see FB_VMODE_* */
__u32 rotate; //反時針旋轉角
__u32 reserved[5]; //留給未來的兼容性
};
3、描述每一像素顯示緩沖區的組織方式結構fb_bitfield
struct fb_bitfield {
__u32 offset; //位域偏移
__u32 length; //位域長度
__u32 msb_right; //MSB(最高有效位)指示
};
4、幀緩沖設備固定參數結構fb_fix_screeninfo
struct fb_fix_screeninfo {
char id[16]; //字符串形式標示符
unsigned long smem_start;//fb緩沖內存的開始地址(物理地址)
__u32 smem_len; //fb緩沖長度
__u32 type; /* see FB_TYPE_* */
__u32 type_aux; /* Interleave for interleaved Planes */
__u32 visual; /* see FB_VISUAL_* */
__u16 xpanstep; /* 硬件平移x */
__u16 ypanstep; /* 硬件平移y */
__u16 ywrapstep; /* zero if no hardware ywrap */
__u32 line_length; /* 行長度 */
unsigned long mmio_start; //內存映射IO的開始地址(物理地址)
__u32 mmio_len; //內存映射IO的長度
__u32 accel;
__u16 reserved[3]; //保留
};
5、幀緩沖設備文件操作函數集合fb_ops
struct fb_ops {
struct module *owner;//模塊所屬
int (*fb_open)(struct fb_info *info, int user);//打開
int (*fb_release)(struct fb_info *info, int user);//釋放
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);
int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);//檢查可變參數
int (*fb_set_par)(struct fb_info *info);//設置video模式
int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,//設置color寄存器
unsigned blue, unsigned transp, struct fb_info *info);
int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);//設置顏色表
int (*fb_blank)(int blank, struct fb_info *info);//顯示空白
int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);//pan顯示
void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);//矩形填充
void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);//數據復制
void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);//圖像填充
int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);//繪制光標
void (*fb_rotate)(struct fb_info *info, int angle);//旋轉顯示
int (*fb_sync)(struct fb_info *info);//等待blit空閑
int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,//IO控制
unsigned long arg);
int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,//處理32位IO控制
unsigned long arg);
int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);//fb特定的mmap
void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,
struct fb_var_screeninfo *var);//根據var得到顯示器的功能信息
void (*fb_destroy)(struct fb_info *info);//銷毀fb
int (*fb_debug_enter)(struct fb_info *info);
int (*fb_debug_leave)(struct fb_info *info);
};
四、幀緩沖子系統初始化過程
在driver/video/fbmem.c中實現了幀緩沖相關的大部分功能
1、幀緩沖子系統初始化
static int __init fbmem_init(void)
{
//創建proc數據
proc_create("fb", 0, NULL, &fb_proc_fops);
//創建並註冊一些以主設備號為29,次設備號為0到256,設備名為fbN的字符設備
if (register_chrdev(FB_MAJOR,"fb",&fb_fops))
printk("unable to get major %d for fb devs\n", FB_MAJOR);
//在sys下創建graphics類
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;
}
__init fbmem_init函數的功能為:
1)創建proc數據
2)創建並註冊一些以主設備號為29,次設備號為0到256,設備名為fbN的字符設備
3)在sys下創建graphics類
//創建並註冊一些以主設備號為29,次設備號為0到256,設備名為fbN的字符設備
static inline int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)
{ //註冊此設備號為0到256的字符設備
return __register_chrdev(major, 0, 256, name, fops);
}
//註冊此設備號為0到256的字符設備
int __register_chrdev(unsigned int major, unsigned int baseminor,
unsigned int count, const char *name,
const struct file_operations *fops)
{
struct char_device_struct *cd;
struct cdev *cdev;
int err = -ENOMEM;
//申請設備號
cd = __register_chrdev_region(major, baseminor, count, name);
if (IS_ERR(cd))
return PTR_ERR(cd);
//分配cdev結構體
cdev = cdev_alloc();
if (!cdev)
goto out2;
//為cdev結構體賦值
cdev->owner = fops->owner;
cdev->ops = fops;
kobject_set_name(&cdev->kobj, "%s", name);
//向內核添加cdev結構
err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);
if (err)
goto out;
cd->cdev = cdev;
return major ? 0 : cd->major;
out:
kobject_put(&cdev->kobj);
out2:
kfree(__unregister_chrdev_region(cd->major, baseminor, count));
return err;
}
2、幀緩沖設備打開方法
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;
//獲得設備文件對應的幀緩沖設備結構,所有註冊的幀緩沖設備fb_info都被保存在registered_fb[FB_MAX]中,最多32個
info = get_fb_info(fbidx);
if (!info) {
//加載幀緩沖設備對應的模塊
request_module("fb%d", fbidx);
//確認模塊是否加載成功
info = get_fb_info(fbidx);
if (!info)
return -ENODEV;
}
if (IS_ERR(info))
return PTR_ERR(info);
mutex_lock(&info->lock);
//嘗試獲得info->fbops所在的模塊
if (!try_module_get(info->fbops->owner)) {
res = -ENODEV;
goto out;
}
//將幀緩沖設備結構作為設備文件的私有數據保存,用於在其它方法中傳遞
file->private_data = info;
if (info->fbops->fb_open) {
//若幀緩沖設備結構中定義了open方法,則調用對應的open方法
res = info->fbops->fb_open(info,1);
if (res)
module_put(info->fbops->owner);
}
#ifdef CONFIG_FB_DEFERRED_IO
if (info->fbdefio)
//若定義了延遲IO操作,調用對應的延遲IO打開函數
fb_deferred_io_open(info, inode, file);
#endif
out:
mutex_unlock(&info->lock);
if (res)
put_fb_info(info);
return res;
}
fb_open函數的功能為:
1)獲得打開的設備文件對應的幀緩沖設備的索引
2)獲得設備文件對應的幀緩沖設備結構
3)加載幀緩沖設備對應的模塊
4)將幀緩沖設備結構作為設備文件的私有數據保存
5)若幀緩沖設備結構中定義了open方法,則調用對應的open方法
6)若定義了延遲IO操作,調用對應的延遲IO打開函數
get_fb_info函數
static struct fb_info *get_fb_info(unsigned int idx)
{
struct fb_info *fb_info;
//幀緩沖設備最多為FB_MAX=32個
if (idx >= FB_MAX)
return ERR_PTR(-ENODEV);
mutex_lock(®istration_lock);
//根據索引從所有註冊的fb_info中找到對應的fb_info結構
fb_info = registered_fb[idx];
if (fb_info)
//增加fb_info的引用次數
atomic_inc(&fb_info->count);
mutex_unlock(®istration_lock);
return fb_info;
}
get_fb_info函數功能為:
1)根據索引從所有註冊的fb_info中找到對應的fb_info結構
2)增加fb_info的引用次數
//保存所有註冊的幀緩沖設備,最多32個,在fb_info註冊方法中將所有註冊的fb_info都保存在registered_fb數組中
struct fb_info *registered_fb[FB_MAX];
3、幀緩沖設備讀方法
static ssize_t fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
unsigned long p = *ppos;
struct fb_info *info = file_fb_info(file);//獲得設備文件對應的幀緩沖設備結構
u8 *buffer, *dst;
u8 __iomem *src;
int c, cnt = 0, err = 0;
unsigned long total_size;
//判斷幀緩沖設備是否為空,虛擬基地址是否為空
if (!info || ! info->screen_base)
return -ENODEV;
//判斷幀緩沖設備是否在運行
if (info->state != FBINFO_STATE_RUNNING)
return -EPERM;
//若幀緩沖設備定義了fb_read方法,調用對應的read方法
if (info->fbops->fb_read)
return info->fbops->fb_read(info, buf, count, ppos);
//獲得ioremapped虛擬內存大小
total_size = info->screen_size;
if (total_size == 0)
total_size = info->fix.smem_len;
if (p >= total_size)
return 0;
if (count >= total_size)
count = total_size;
if (count + p > total_size)
count = total_size - p;
//分配緩沖區
buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
GFP_KERNEL);
if (!buffer)
return -ENOMEM;
//獲得需要操作的虛擬基地址
src = (u8 __iomem *) (info->screen_base + p);
//調用等待blit空閑方法
if (info->fbops->fb_sync)
info->fbops->fb_sync(info);
while (count) {
c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
dst = buffer;
//從物理顯示緩沖區讀取數據
fb_memcpy_fromfb(dst, src, c);
dst += c;
src += c;
//復制數據到用戶空間
if (copy_to_user(buf, buffer, c)) {
err = -EFAULT;
break;
}
*ppos += c;
buf += c;
cnt += c;
count -= c;
}
kfree(buffer);
return (err) ? err : cnt;
}
fb_read函數功能為:
1)獲得設備文件對應的幀緩沖設備結構
2)判斷幀緩沖設備是否為空,虛擬基地址是否為空;判斷幀緩沖設備是否在運行,
若幀緩沖設備定義了fb_read方法,調用對應的read方法
3)若未定義fb_read方法,獲得ioremapped虛擬內存大小,分配緩沖區
4)獲得需要操作的虛擬基地址,調用等待blit空閑方法
5)從物理顯示緩沖區讀取數據,復制數據到用戶空間
file_fb_info函數
static struct fb_info *file_fb_info(struct file *file)
{
struct inode *inode = file->f_path.dentry->d_inode;//獲得設備節點
int fbidx = iminor(inode);//根據設備節點獲得fb_info結構索引
struct fb_info *info = registered_fb[fbidx];//根據索引從所有註冊的fb_info中找到對應的fb_info結構
if (info != file->private_data)
info = NULL;
return info;
}
file_fb_info函數的功能為根據設備文件獲得設備節點,根據設備節點獲得fb_info結構索引,根據索引從所有註冊的
fb_info中找到對應的fb_info結構
fb_memcpy_fromfb函數
#define fb_memcpy_fromfb memcpy_fromio
void memcpy_fromio(void *to, const volatile void __iomem *from, long count)
{
char *dst = to;
while (count) {
count--;
*dst++ = readb(from++);//讀取IO內存
}
}
EXPORT_SYMBOL(memcpy_fromio);
memcpy_fromio函數的功能為從IO空間讀取數據到內存
4、幀緩沖設備寫方法
static ssize_t fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
unsigned long p = *ppos;
struct fb_info *info = file_fb_info(file);//獲得設備文件對應的幀緩沖設備結構
u8 *buffer, *src;
u8 __iomem *dst;
int c, cnt = 0, err = 0;
unsigned long total_size;
//判斷幀緩沖設備是否為空,虛擬基地址是否為空
if (!info || !info->screen_base)
return -ENODEV;
//判斷幀緩沖設備是否在運行
if (info->state != FBINFO_STATE_RUNNING)
return -EPERM;
//若幀緩沖設備定義了fb_write方法,調用對應的write方法
if (info->fbops->fb_write)
return info->fbops->fb_write(info, buf, count, ppos);
//獲得ioremapped虛擬內存大小
total_size = info->screen_size;
if (total_size == 0)
total_size = info->fix.smem_len;
if (p > total_size)
return -EFBIG;
if (count > total_size) {
err = -EFBIG;
count = total_size;
}
if (count + p > total_size) {
if (!err)
err = -ENOSPC;
count = total_size - p;
}
//分配緩沖區
buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
GFP_KERNEL);
if (!buffer)
return -ENOMEM;
//獲得需要操作的虛擬基地址
dst = (u8 __iomem *) (info->screen_base + p);
//調用等待blit空閑方法
if (info->fbops->fb_sync)
info->fbops->fb_sync(info);
while (count) {
c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
src = buffer;
//從用戶空間復制數據到分配的緩沖區
if (copy_from_user(src, buf, c)) {
err = -EFAULT;
break;
}
//復制緩沖區的內容到物理顯示緩沖區中
fb_memcpy_tofb(dst, src, c);
dst += c;
src += c;
*ppos += c;
buf += c;
cnt += c;
count -= c;
}
kfree(buffer);
return (cnt) ? cnt : err;
}
fb_write函數功能為:
1)獲得設備文件對應的幀緩沖設備結構
2)判斷幀緩沖設備是否為空,虛擬基地址是否為空;判斷幀緩沖設備是否在運行,
若幀緩沖設備定義了fb_write方法,調用對應的write方法
3)若未定義fb_write方法,獲得ioremapped虛擬內存大小,分配緩沖區
4)獲得需要操作的虛擬基地址,調用等待blit空閑方法
5)從用戶空間復制數據到分配的緩沖區,復制緩沖區的內容到物理顯示緩沖區中
fb_memcpy_tofb函數
#define fb_memcpy_tofb memcpy_toio
void memcpy_toio(volatile void __iomem *to, const void *from, long count)
{
const char *src = from;
while (count) {
count--;
writeb(*src++, to++);//寫入IO內存
}
}
EXPORT_SYMBOL(memcpy_toio);
memcpy_toio函數的功能為將內存空間的數據寫入到IO空間
5、幀緩沖設備內存映射方法
static int fb_mmap(struct file *file, struct vm_area_struct * vma)
{
struct fb_info *info = file_fb_info(file);//獲得設備文件對應的幀緩沖設備結構
struct fb_ops *fb;
unsigned long off;
unsigned long start;
u32 len;
if (!info)
return -ENODEV;
if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
return -EINVAL;
off = vma->vm_pgoff << PAGE_SHIFT;
fb = info->fbops;//獲得fb_info的操作函數集合
if (!fb)
return -ENODEV;
mutex_lock(&info->mm_lock);
if (fb->fb_mmap) {//若fb_info中定義了fb_mmap方法
int res;
//調用fb_info中的fb_mmap方法
res = fb->fb_mmap(info, vma);
mutex_unlock(&info->mm_lock);
return res;
}
/* frame buffer memory */
start = info->fix.smem_start;
len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);
if (off >= len) {
/* memory mapped io */
off -= len;
if (info->var.accel_flags) {
mutex_unlock(&info->mm_lock);
return -EINVAL;
}
start = info->fix.mmio_start;
len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);
}
mutex_unlock(&info->mm_lock);
start &= PAGE_MASK;
if ((vma->vm_end - vma->vm_start + off) > len)
return -EINVAL;
off += start;
vma->vm_pgoff = off >> PAGE_SHIFT;
/* This is an IO map - tell maydump to skip this VMA */
vma->vm_flags |= VM_IO | VM_RESERVED;
vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
fb_pgprotect(file, vma, off);
if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
vma->vm_end - vma->vm_start, vma->vm_page_prot))
return -EAGAIN;
return 0;
}
五、幀緩沖設備註冊
int register_framebuffer(struct fb_info *fb_info)
{
int ret;
mutex_lock(®istration_lock);
//調用do_register_framebuffer函數註冊幀緩沖設備
ret = do_register_framebuffer(fb_info);
mutex_unlock(®istration_lock);
return ret;
}
register_framebuffer函數調用do_register_framebuffer方法註冊幀緩沖設備
do_register_framebuffer函數
static int do_register_framebuffer(struct fb_info *fb_info)
{
int i;
struct fb_event event;
struct fb_videomode mode;
//檢測幀緩沖設備的標誌位
if (fb_check_foreignness(fb_info))
return -ENOSYS;
//註銷有沖突的幀緩沖設備
do_remove_conflicting_framebuffers(fb_info->apertures, fb_info->fix.id,
fb_is_primary_device(fb_info));
if (num_registered_fb == FB_MAX)
return -ENXIO;
num_registered_fb++;//全局變量,記錄註冊的幀緩沖設備數
for (i = 0 ; i < FB_MAX; i++)
if (!registered_fb[i])
break;
fb_info->node = i;//設置設備節點
atomic_set(&fb_info->count, 1);//設備引用計數為1
mutex_init(&fb_info->lock);
mutex_init(&fb_info->mm_lock);
//在/dev中創建設備文件
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
//初始化dev設備文件
fb_init_device(fb_info);
if (fb_info->pixmap.addr == NULL) {
//分配圖形映射區域
fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);
//為fb_iinfo結構的圖形映射區域賦值
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);
//將幀緩沖設備的可變參數轉化為video模式
fb_var_to_videomode(&mode, &fb_info->var);
//添加video模式入口到mode列表中
fb_add_videomode(&mode, &fb_info->modelist);
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;
}
do_register_framebuffer函數的功能為
1)檢測幀緩沖設備的標誌位
2)註銷有沖突的幀緩沖設備
3)增加全局幀緩沖設備數;設置註冊的幀緩沖設備節點,設備引用計數為1
4)在/dev中創建設備文件,初始化dev設備文件
5)分配圖形映射區域,為fb_iinfo結構的圖形映射區域賦值
6)將幀緩沖設備的可變參數轉化為video模式,添加video模式入口到mode列表中
7)將註冊的幀緩沖設備添加全局幀緩沖設備數組中,註冊客戶端通知
fb_check_foreignness函數功能為檢測fb_info的標誌位
static int fb_check_foreignness(struct fb_info *fi)
{
const bool foreign_endian = fi->flags & FBINFO_FOREIGN_ENDIAN;
fi->flags &= ~FBINFO_FOREIGN_ENDIAN;
#ifdef __BIG_ENDIAN
fi->flags |= foreign_endian ? 0 : FBINFO_BE_MATH;
#else
fi->flags |= foreign_endian ? FBINFO_BE_MATH : 0;
#endif /* __BIG_ENDIAN */
if (fi->flags & FBINFO_BE_MATH && !fb_be_math(fi)) {
pr_err("%s: enable CONFIG_FB_BIG_ENDIAN to "
"support this framebuffer\n", fi->fix.id);
return -ENOSYS;
} else if (!(fi->flags & FBINFO_BE_MATH) && fb_be_math(fi)) {
pr_err("%s: enable CONFIG_FB_LITTLE_ENDIAN to "
"support this framebuffer\n", fi->fix.id);
return -ENOSYS;
}
return 0;
}
do_remove_conflicting_framebuffers函數的功能為註銷有沖突的幀緩沖設備
static void do_remove_conflicting_framebuffers(struct apertures_struct *a,
const char *name, bool primary)
{
int i;
/* check all firmware fbs and kick off if the base addr overlaps */
//檢測所有幀緩沖固件,去除基地址重疊的幀緩沖設備
for (i = 0 ; i < FB_MAX; i++) {
struct apertures_struct *gen_aper;
if (!registered_fb[i])
continue;
if (!(registered_fb[i]->flags & FBINFO_MISC_FIRMWARE))
continue;
gen_aper = registered_fb[i]->apertures;
if (fb_do_apertures_overlap(gen_aper, a) ||
(primary && gen_aper && gen_aper->count &&
gen_aper->ranges[0].base == VGA_FB_PHYS)) {
printk(KERN_INFO "fb: conflicting fb hw usage "
"%s vs %s - removing generic driver\n",
name, registered_fb[i]->fix.id);
do_unregister_framebuffer(registered_fb[i]);
}
}
}
六、總結
通過以上分析可知,幀緩沖設備實質是字符設備,內核抽象了一個fb_info結構用來描述幀緩沖設備,通過registered_fb數組
保存所有註冊的fb_info結構,num_registered_fb表示註冊的fb_info結構數。內核實現了字符設備對用戶空間的接口,當調用
接口函數時,內核會判斷對應的fb_info是否實現了對應的方法,若實現了,就會調用fb_info結構中定義的方法,若未實現,
則進行內核的默認處理。
因此實現幀緩沖設備的過程是,分配fb_info結構,填充fb_info結構成員,向內核註冊fb_info結構。其他功能內核代碼已經實現
限會員,要發表迴響,請先登入


