Framebuffer對應的源文件在linux/drivers/video/目錄下。總的抽象設備文件為fbcon.c,在這個目錄下還有與各種顯卡驅動相關的源文件。
(一)、分析Framebuffer設備驅動
需要特別提出的是在INTEL平台上,老式的VESA1.2卡,如CGA/EGA卡,是不能支持Framebuffer的,因為Framebuffer要求顯卡支持線性幀緩沖,即CPU可以訪問顯緩沖中的每一位, 但是VESA1.2 卡只能允許CPU一次訪問64K的地址空間。
FrameBuffer設備驅動基於如下兩個文件:
1) linux/include/linux/fb.h
2) linux/drivers/video/fbmem.c 下面分析這兩個文件。
1、fb.h
幾乎主要的結構都是在這個中文件定義的。這些結構包括:
1)fb_var_screeninfo
這個結構描述了顯示卡的特性:
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 resolution */ __u32 yoffset; __u32 bits_per_pixel; /* guess what */ __u32 grayscale; /* != 0 Gray levels 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 */ __u32 width; /* width of picture in mm */ __u32 accel_flags; /* acceleration flags (hints) */ /* Timing: All values in pixclocks, except pixclock (of course) */ __u32 pixclock; /* pixel clock in ps (pico seconds) */ __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 reserved[6]; /* Reserved for future compatibility */ };
2) fb_fix_screeninfon
這個結構在顯卡被設定模式後創建,它描述顯示卡的屬性,並且系統運行時不能被修改;比如FrameBuffer內存的起始地址。它依賴於被設定的模式,當一個模 式被設定後,內存信息由顯示卡硬件給出,內存的位置等信息就不可以修改。
struct fb_fix_screeninfo { char id[16]; /* identification string eg "TT Builtin" */ unsigned long smem_start; /* Start of frame buffer mem */ /* (physical address) */ __u32 smem_len; /* Length of frame buffer mem */ __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 */ unsigned long mmio_start; /* Start of Memory Mapped I/O */ /* (physical address) */ __u32 mmio_len; /* Length of Memory Mapped I/O */ __u32 accel; /* Type of acceleration available */ __u16 reserved[3]; /* Reserved for future compatibility */ };
3) fb_cmap
描述設備無關的顏色映射信息。可以通過FBIOGETCMAP 和 FBIOPUTCMAP 對應的ioctl操作設定或獲取顏色映射信息.
struct fb_cmap { __u32 start; /* First entry */ __u32 len; /* Number of entries */ __u16 *red; /* Red values */ __u16 *green; __u16 *blue; __u16 *transp; /* transparency, can be NULL */ };
4) fb_info
定義當顯卡的當前狀態;fb_info結構僅在內核中可見,在這個結構中有一個fb_ops指針, 指向驅動設備工作所需的函數集。
struct fb_info { char modename[40]; /* default video mode */ kdev_t node; int flags; int open; /* Has this been open already ? */ #define FBINFO_FLAG_MODULE 1 /* Low-level driver is a module */ struct fb_var_screeninfo var; /* Current var */ struct fb_fix_screeninfo fix; /* Current fix */ struct fb_monspecs monspecs; /* Current Monitor specs */ struct fb_cmap cmap; /* Current cmap */ struct fb_ops *fbops; char *screen_base; /* Virtual address */ struct display *disp; /* initial display variable */ struct vc_data *display_fg; /* Console visible on this display */ char fontname[40]; /* default font name */ devfs_handle_t devfs_handle; /* Devfs handle for new name */ devfs_handle_t devfs_lhandle; /* Devfs handle for compat. symlink */ int (*changevar)(int); /* tell console var has changed */ int (*switch_con)(int, struct fb_info*); /* tell fb to switch consoles */ int (*updatevar)(int, struct fb_info*); /* tell fb to update the vars */ void (*blank)(int, struct fb_info*); /* tell fb to (un)blank the screen */ /* arg = 0: unblank */ /* arg > 0: VESA level (arg-1) */ void *pseudo_palette; /* Fake palette of 16 colors and the cursor's color for non palette mode */ /* From here on everything is device dependent */ void *par; };
5) struct fb_ops
用戶應用可以使用ioctl()系統調用來操作設備,這個結構就是用一支持ioctl()的這些操作的。
struct fb_ops { /* 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); /* get non settable parameters */ int (*fb_get_fix)(struct fb_fix_screeninfo *fix, int con, struct fb_info *info); /* get settable parameters */ int (*fb_get_var)(struct fb_var_screeninfo *var, int con, struct fb_info *info); /* set settable parameters */ int (*fb_set_var)(struct fb_var_screeninfo *var, int con, struct fb_info *info); /* get colormap */ int (*fb_get_cmap)(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info); /* set colormap */ int (*fb_set_cmap)(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info); /* pan display (optional) */ int (*fb_pan_display)(struct fb_var_screeninfo *var, int con, struct fb_info *info); /* perform fb specific ioctl (optional) */ int (*fb_ioctl)(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg, int con, struct fb_info *info); /* perform fb specific mmap */ int (*fb_mmap)(struct fb_info *info, struct file *file, struct vm_area_struct *vma); /* switch to/from raster image mode */ int (*fb_rasterimg)(struct fb_info *info, int start); };
6) structure map
struct fb_info_gen | struct fb_info | fb_var_screeninfo
| | fb_fix_screeninfo
| | fb_cmap
| | modename[40]
| | fb_ops ---|--->ops on var
| | ... | fb_open
| | | fb_release
| | | fb_ioctl
| | | fb_mmap
| struct fbgen_hwswitch -|-> detect
| | encode_fix
| | encode_var
| | decode_fix
| | decode_var
| | get_var
| | set_var
| | getcolreg
| | setcolreg
| | pan_display
| | blank
| | set_disp
[編排有點困難,第一行的第一條豎線和下面的第一列豎線對齊,第一行的第二條豎線和下面的第二列豎線對齊就可以了]
這個結構 fbgen_hwswitch抽象了硬件的操作.雖然它不是必需的,但有時候很有用.
2、 fbmem.c
fbmem.c 處於Framebuffer設備驅動技術的中心位置.它為上層應用程序提供系統調用也為下一層的特定硬件驅動提供接口;那些底層硬件驅動需要用到這兒的接口來向 系統內核注冊它們自己.
fbmem.c 為所有支持FrameBuffer的設備驅動提供了通用的接口,避免重復工作.
1) 全局變量
struct fb_info *registered_fb[FB_MAX]; int num_registered_fb;
這兩變量記錄了所有fb_info 結構的實例,fb_info 結構描述顯卡的當前狀態,所有設備對應的fb_info結構都保存在這個數組中,當一個FrameBuffer設備驅動向系統注冊自己時,其對應的fb_info結構就會添加到這個結構中,同時num_registered_fb 為自動加1.
static struct { const char *name; int (*init)(void); int (*setup)(void); } fb_drivers[] __initdata= { ....};
如果FrameBuffer設備被靜態鏈接到內核,其對應的入口就會添加到這個表中;如果是動態加載的,即使用insmod/rmmod,就不需要關心這個表。
static struct file_operations fb_ops ={ owner: THIS_MODULE, read: fb_read, write: fb_write, ioctl: fb_ioctl, mmap: fb_mmap, open: fb_open, release: fb_release };
這是一個提供給應用程序的接口.
2)fbmem.c 實現了如下函數.
register_framebuffer(struct fb_info *fb_info); unregister_framebuffer(struct fb_info *fb_info);
這兩個是提供給下層FrameBuffer設備驅動的接口,設備驅動通過這兩函數向系統注冊或注銷自己。幾乎底層設備驅動所要做的所有事情就是填充fb_inf o結構然後向系統注冊或注銷它。
(二)一個LCD顯示芯片的驅動實例
以Skeleton LCD控制器驅動為例,在LINUX中存有一個/fb/skeleton.c的skeleton的Framebuffer驅動程序,很簡單,僅僅是填充了fb_info結構,並且注冊/注銷自己。設備驅動是向用戶程序提供系統調用接口,所以我們需要實現底層硬件操作並且定義file_operations結構來向系統提供系統調用接口,從而實現更有效的LCD控制器驅動程序。
1)在系統內存中分配顯存
在fbmem.c文件中可以看到,file_operations結構中的open()和release()操作不需底層支持,但read()、write()和 mmap()操作需要函數fb_get_fix()的支持.因此需要重新實現函數fb_get_fix()。另外還需要在系統內存中分配顯存空間,大多數的LCD控制器都沒有自己的顯存空間,被分配的地址空間的起 始地址與長度將會被填充到fb_fix_screeninfo結構的smem_start 和smem_len 的兩個變量中.被分配的空間必須是物理連續的。
2)實現 fb_ops 中的函數
用戶應用程序通過ioctl()系統調用操作硬件,fb_ops 中的函數就用於支持這些操作。(注: fb_ops結構與file_operations結構不同,fb_ops是底層操作的抽象,而file_operations是提供給上層系統調用的接口,可以直接調用ioctl()系統調用在文件fbmem.c中實現,通過觀察可以發現ioctl()命令與fb_ops's 中函數的關系:
FBIOGET_VSCREENINFO fb_get_var FBIOPUT_VSCREENINFO fb_set_var FBIOGET_FSCREENINFO fb_get_fix FBIOPUTCMAP fb_set_cmap FBIOGETCMAP fb_get_cmap FBIOPAN_DISPLAY fb_pan_display
如果我們定義了fb_XXX_XXX 方法,用戶程序就可以使用FBIOXXXX宏的ioctl()操作來操作硬件。
文件linux/drivers/video/fbgen.c或者linux/drivers/video目錄下的其它設備驅動是比較好的參考資料。在所有的這 些函數中fb_set_var()是最重要的,它用於設定顯示卡的模式和其它屬性,下面是函數fb_set_var()的執行步驟:
1)檢測是否必須設定模式
2)設定模式
3)設定顏色映射
4) 根據以前的設定重新設置LCD控制器的各寄存器。
第四步表明了底層操作到底放置在何處。在系統內存中分配顯存後,顯存的起始地址及長度將被設定到LCD控制器的各寄存器中(一般通過fb_set_var()函數),顯存中的內容將自動被LCD控制器輸出到屏幕上。另一方面,用戶程序通過函數mmap()將顯存映射到用戶進程地址空間中,然後用戶進程向映射空間發送 的所有數據都將會被顯示到LCD顯示器上。