利用framebuffer和tty在linux编写图形驱动
时间:2010-10-18 来源:cywcdwxjf
虽然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 */
嘿嘿。
由于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 */
嘿嘿。
相关阅读 更多 +