利用framebuffer和tty在linux编写图形驱动

虽然framebuffer有很多局限性,比如在内核启动之后就无法修改分辨率,但是通过framebuffer这个内核抽象出来的设备文件可以很方便的控制显卡,显示图像。

由于framebuffer是显卡的抽象,因此向 /dev/fb0(假设是fb0),就相当于向显存写数据,因此无论你是在什么环境下屏幕都会被覆盖掉,但是同时还有其他进程和你争抢显卡的显示权,因此需要一种方式独占显卡的使用权限,这个也是我找了很久才发现的。

对framebuffer的操作很容易从网上找到例子,我这里简单说一下

1.在未开启framebuffer的linux系统下开启framebuffer(以Ubuntu 9.04alpha6为例)

开启framebuffer需要修改以下几个文件,你可以在在命令行里用vi编辑,也可以个用gedit,我个人还是推荐后者。

sudo gedit /etc/initramfs-tools/modules

在最后面新起两行加入

fbcon
vesafb

sudo gedit /etc/modprobe.d/blacklist-framebuffer.conf

注释掉(前面添加一个#)

blacklist vesafb

同时还要注释掉你的显卡驱动,我的是nVidia

sudo gedit /boot/grub/menu.lst

在kernel启动参数的行末尾添上 vga=0x317 (1024x768,16位,你也可以使用别的分辨率模式)

然后

sudo update-initramfs -u

重新启动就可以了!

2.framebuffer的基本操作

一下代码均是我写的源代码,绝对是可以编译通过的,至于运行效果我只在我自己的电脑上测试过有效。

基本预处理操作

包含头文件
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
//要打开的framebuffer的编号,默认是0
    char devname[50];
   
    autorefresh=false;

    sprintf(devname,"/dev/fb%d",num);
   
    fd=open(devname,O_RDWR);
   
    if(fd<0)
    {
        fd=-1;
#ifdef DEBUG
        printf("open %s error!\n",devname);
#endif
         return;
    }
    //获取信息
    if(fb::refreshinfo())
    {
         //映射内存
         mem = (char*)mmap(0,finfo.smem_len,PROT_READ | PROT_WRITE,MAP_SHARED,fd, 0);
         if((int)mem==-1)
         {
#ifdef DEBUG
            printf("reflect memory error! \n");
#endif
            mem=NULL;
        }
    }
    else
    {
#ifdef DEBUG
         printf("can not reflect memory!\n");
#endif
        mem=NULL;
    }

/*---------------------------------------------------------------------------------------------------------------*/
//更新fb信息
   
    if(ioctl(fd,FBIOGET_FSCREENINFO,&finfo))
    {
#ifdef DEBUG
        printf("Reading fb_fix_screeninfo error!\n");
#endif
        return false;
    }

    if(ioctl(fd,FBIOGET_VSCREENINFO,&vinfo))
    {
#ifdef DEBUG
        printf("Reading fb_var_screeninfo error!\n");
#endif
        return false;
    }
    return true;

这两个是最重要的,怎么样,简单吧?至于修改参数,我按照网上参考的源代码去做没有效果,待我研究透彻再搬上来。

如何独占显卡?我在跟踪mplayer在fbdev方式下播放时的操作发现的,原理是这样的:

mplayer首先找到当前活动的终端,然后打开该终端,保存当前终端的状态,然后修改状态,最后把终端切换到图形模式,这是他就获得了控制权,即使你按下ctrl+alt+fn切换终端也是无效的。

但是这其中还有一个问题没有解决,无论是我写的程序,还是mplayer在x.org的图形终端里无法正常显示、播放图像、视频。我记得x.org没有使 用framebuffer,他有自己的一套驱动,但是显卡只有一个,他的驱动如何和framebuffer协同工作,这其中我不是很清楚,在x.org里 (其实是xsever)里可以修改分辨率的,当然正宗的针对nVidia的驱动做到这点没什么神奇的,问题是framebuffer的分辨率和他不一样, 也就是说framebuffer和xsever驱动不可以同时运行,但是我确实赋值成功了,而且屏幕确实改变了,只不过不是想象中的图像。

好了,下面写操作虚拟终端的代码,当然,你也可一不使用当前终端,可以创建一个新的终端,创建新终端很简单,只要打开相应的设备文件即可。

头文件
#include<linux/vt.h>
#include<linux/kd.h>
//获取当前活动的虚拟终端
    int kd;
    struct vt_stat vts;
    kd=open("/dev/tty0", O_WRONLY, 0);
    if(kd<0)
    {
        //打开失败
#ifdef DEBUG
        printf("Open /dev/tty0 error!");
#endif       
        return lge::gphdev::gphdevice::tty(-1);        //-1表示不初始化
        //产生错误
    }
   
    if (ioctl(kd, VT_GETSTATE, &vts) == 0)
    {
        close(kd);
        return lge::gphdev::gphdevice::tty(vts.v_active);
    }
    else
    {
        //获取状态失败
#ifdef DEBUG
        printf("ioctl VT_GETSTATE error!\n");
#endif
        close(kd);
        return lge::gphdev::gphdevice::tty(-1);
    }
/*----------------------------------------------------------------------*/
char devname[50];
    vtsaved=false;
    curnum=ttynum;
    if(ttynum==-1)
    {
        //不初始化
        fd=-1;
       
        return;
    }
   
    //打开
   
    sprintf(devname,"/dev/tty%d",ttynum);
    fd=open(devname, O_RDWR | O_NDELAY, 0);
    if(fd<0)
    {
        //打开文件失败
#ifdef DEBUG
        printf("open %s error!\n",devname);
#endif
        fd=-1;
        curnum=-1;
        return;
    }
/*---------------------------------------------------------------------------*/
//保存这个虚拟终端的状态
    if(fd==-1)
        return false;
    if (ioctl(fd, VT_GETMODE, &o_vt) < 0)
    {
#ifdef DEBUG
        printf("ioctl VT_GETMODE error!\n");
#endif
        return false;
    }
    vtsaved=true;
    return true;
/*---------------------------------------------------------------------------*/
//从保存的状态中恢复
    if(fd==-1 || vtsaved==false)
        return false;
    if(ioctl(fd, VT_SETMODE, &o_vt)<0)
    {
#ifdef DEBUG
        printf("ioctl VT_SETMODE error!\n");
#endif
        return false;
    }
    return true;
/*---------------------------------------------------------------------------*/
//文字模式
    if(fd==-1)
        return false;
    if(ioctl(fd, KDSETMODE, KD_TEXT)<0)
    {
#ifdef DEBUG
        printf("ioctl KDSETMODE KD_TEXT error!\n");
#endif
        return false;
    }
    return true;
/*--------------------------------------------------------------------------*/
//图形模式
    struct vt_mode vt;
   
    if(fd==-1)
        return false;
//??
    vt.mode = VT_PROCESS;
    vt.relsig = SIGUSR1;
    vt.acqsig = SIGUSR1;
//--
    if(ioctl(fd, VT_SETMODE, &vt) < 0)
    {
#ifdef DEBUG
        printf("ioctl VT_SETMODE error!\n");
#endif
        return false;
    }
   
    //修改显示模式
    if (ioctl(fd, KDSETMODE, KD_GRAPHICS) < 0)
    {
#ifdef DEBUG
        printf("ioctl KDSETMODE KD_GRAPHICS error!\n");
#endif
        return false;
    }
    return true;

/*--------------------------------------------------------------------------*/
其中fd是在第一个构造函数里打开的终端文件的句柄

简单讲一下ioctl用于和驱动程序交换信息,至于把某结构修改成某某值大家先这样做把,尤其是
//??
    vt.mode = VT_PROCESS;
    vt.relsig = SIGUSR1;
    vt.acqsig = SIGUSR1;
//--
很重要,否则会使它失去响应的,至于为什么……你没看到我也画问号了吗~

哦~忘记说了
#define SIGUSR1 10
这个不要忘。

测试吗~可以简单向影射好的内存地址memset一堆0你就看到屏幕黑了,嘿嘿,至于显示图像嘛~我也就能解码bmp的,不敢献丑勒…

PS:再告诉大家一个好方法,看到不懂地方就去看看头文件,注释很详细的,而且编写fb.h的大牛很搞笑

在结构
struct fb_var_screeninfo
有一句
    __u32 bits_per_pixel;        /* guess what            */
嘿嘿。

作者: cywcdwxjf   发布时间: 2010-10-18