Contents ...
udn網路城邦
分析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結構。其他功能內核代碼已經實現
全站分類:知識學習 科學百科
自訂分類:不分類
上一則: linux定時器
下一則: linux內核中的BUG和BUG_ON

限會員,要發表迴響,請先登入