#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#define PERFORMANCE_RUN_COUNT 5
#ifndef __ANDROID__
#define FBDEV "/dev/fb0"
#define BMP_PIC_DIR "./Test.bmp"
#define OUT_DIR "./"
#else
#define FBDEV "/dev/graphics/fb0"
#define OUT_DIR "./"
#endif
struct fb_var_screeninfo vinfo;
struct fb_fix_screeninfo finfo;
static char *frameBuffer = 0;
static int xres = 0;
static int yres = 0;
static int bits_per_pixel = 0;
unsigned long screensize = 0;
//14byte文件头
typedef struct {
char cfType[2]; //文件类型,"BM"(0x4D42)
long cfSize; //文件大小(字节)
long cfReserved; //保留,值为0
long cfoffBits; //数据区相对于文件头的偏移量(字节)
}__attribute__((packed)) BITMAPFILEHEADER;
//__attribute__((packed))的作用是告诉编译器取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐
//40byte信息头
typedef struct {
char ciSize[4]; //BITMAPFILEHEADER所占的字节数
long ciWidth; //水平像素点个数
long ciHeight; //垂直像素点个数
char ciPlanes[2]; //目标设备的位平面数,值为1
int ciBitCount; //每个像素的位数
char ciCompress[4]; //压缩说明
char ciSizeImage[4]; //用字节表示的图像大小,该数据必须是4的倍数
char ciXPelsPerMeter[4];//目标设备的水平像素数/米
char ciYPelsPerMeter[4];//目标设备的垂直像素数/米
char ciClrUsed[4]; //位图使用调色板的颜色数
char ciClrImportant[4]; //指定重要的颜色数,当该域的值等于颜色数时(或者等于0时),表示所有颜色都一样重要
}__attribute__((packed)) BITMAPINFOHEADER;
typedef struct {
unsigned char red:8;
unsigned char green:8;
unsigned char blue:8;
}__attribute__((packed)) PIXEL; //颜色模式,RGB888
BITMAPFILEHEADER FileHead;
BITMAPINFOHEADER InfoHead;
//打印fb驱动中fix结构信息,注:在fb驱动加载后,fix结构不可被修改。
void printFixedInfo() {
printf("Fixed screen info:\n"
"\tid: %s\n"
"\tsmem_start: 0x%lx\n"
"\tsmem_len: %d\n"
"\ttype: %d\n"
"\ttype_aux: %d\n"
"\tvisual: %d\n"
"\txpanstep: %d\n"
"\typanstep: %d\n"
"\tywrapstep: %d\n"
"\tline_length: %d\n"
"\tmmio_start: 0x%lx\n"
"\tmmio_len: %d\n"
"\taccel: %d\n"
"\n", finfo.id, finfo.smem_start, finfo.smem_len, finfo.type,
finfo.type_aux, finfo.visual, finfo.xpanstep, finfo.ypanstep,
finfo.ywrapstep, finfo.line_length, finfo.mmio_start,
finfo.mmio_len, finfo.accel);
}
//打印fb驱动中var结构信息,注:fb驱动加载后,var结构可根据实际需要被重置
void printVariableInfo() {
printf("Variable screen info:\n"
"\txres: %d\n"
"\tyres: %d\n"
"\txres_virtual: %d\n"
"\tyres_virtual: %d\n"
"\tyoffset: %d\n"
"\txoffset: %d\n"
"\tbits_per_pixel: %d\n"
"\tgrayscale: %d\n"
"\tred: offset: -, length: -, msb_right: -\n"
"\tgreen: offset: -, length: -, msb_right: -\n"
"\tblue: offset: -, length: -, msb_right: -\n"
"\ttransp: offset: -, length: -, msb_right: -\n"
"\tnonstd: %d\n"
"\tactivate: %d\n"
"\theight: %d\n"
"\twidth: %d\n"
"\taccel_flags: 0x%x\n"
"\tpixclock: %d\n"
"\tleft_margin: %d\n"
"\tright_margin: %d\n"
"\tupper_margin: %d\n"
"\tlower_margin: %d\n"
"\thsync_len: %d\n"
"\tvsync_len: %d\n"
"\tsync: %d\n"
"\tvmode: %d\n"
"\n", vinfo.xres, vinfo.yres, vinfo.xres_virtual,
vinfo.yres_virtual, vinfo.xoffset, vinfo.yoffset,
vinfo.bits_per_pixel, vinfo.grayscale, vinfo.red.offset,
vinfo.red.length, vinfo.red.msb_right, vinfo.green.offset,
vinfo.green.length, vinfo.green.msb_right, vinfo.blue.offset,
vinfo.blue.length, vinfo.blue.msb_right, vinfo.transp.offset,
vinfo.transp.length, vinfo.transp.msb_right, vinfo.nonstd,
vinfo.activate, vinfo.height, vinfo.width, vinfo.accel_flags,
vinfo.pixclock, vinfo.left_margin, vinfo.right_margin,
vinfo.upper_margin, vinfo.lower_margin, vinfo.hsync_len,
vinfo.vsync_len, vinfo.sync, vinfo.vmode);
}
void performWriteSpeedTest(void *fb, int fbSize) {
int i, j, run;
struct timeval startTime, endTime;
unsigned long long results[PERFORMANCE_RUN_COUNT];
unsigned long long average;
unsigned int *testImage;
unsigned int randData[17] = { 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff,
0xff000000, 0xff000000, 0xff000000,
0xff000000, 0xff000000, 0xff000000,
0xff000000,0xff000000 };
printf("Frame Buffer Performance write test...\n");
for (run = 0; run < PERFORMANCE_RUN_COUNT; ++run) {
testImage = (unsigned int *) malloc(fbSize);
j = run;
for (i = 0; i < (int) (fbSize / sizeof(int)); ++i) {
testImage[i] = randData[j];
j++;
if (j >= 17)
j = 0;
}
gettimeofday(&startTime, NULL);
memcpy(fb, testImage, fbSize);
gettimeofday(&endTime, NULL);
long secsDiff = endTime.tv_sec - startTime.tv_sec;
results[run] = secsDiff * 1000000
+ (endTime.tv_usec - startTime.tv_usec);
free(testImage);
}
average = 0;
for (i = 0; i < PERFORMANCE_RUN_COUNT; ++i)
average += results[i];
average = average / PERFORMANCE_RUN_COUNT;
printf(" Average: %llu usecs\n", average);
printf(" Bandwidth: %.03f MByte/Sec\n",
(fbSize / 1048576.0) / ((double) average / 1000000.0));
printf(" Max. FPS: %.03f fps\n\n", 1000000.0 / (double) average);
memset(fb, 0, fbSize);
}
static unsigned char sg_BHeader[] =
{
0x42, 0x4D, 0x36, 0xEC, 0x5E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00,
0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x38, 0x04, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x0B, 0x00, 0x00, 0x12, 0x0B, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
void SaveBMPFile(unsigned char *raw, char *filename) {
unsigned long *p = (unsigned long *)raw;
typedef unsigned int UINT;
typedef unsigned char UCHAR;
UINT m_Width = 1920, m_Height = 1080;
UINT i, j;
int bmp = open(filename, O_WRONLY | O_CREAT);
if(bmp < 0)
return;
sg_BHeader[0x02] = (UCHAR)(m_Width * m_Height * 3 + 0x36) & 0xff;
sg_BHeader[0x03] = (UCHAR)((m_Width * m_Height * 3 + 0x36) >> 8) & 0xff;
sg_BHeader[0x04] = (UCHAR)((m_Width * m_Height * 3 + 0x36) >> 16) & 0xff;
sg_BHeader[0x05] = (UCHAR)((m_Width * m_Height * 3 + 0x36) >> 24) & 0xff;
sg_BHeader[0x12] = (UCHAR)m_Width & 0xff;
sg_BHeader[0x13] = (UCHAR)(m_Width >> 8) & 0xff;
sg_BHeader[0x14] = (UCHAR)(m_Width >> 16) & 0xff;
sg_BHeader[0x15] = (UCHAR)(m_Width >> 24) & 0xff;
sg_BHeader[0x16] = (UCHAR)m_Height & 0xff;
sg_BHeader[0x17] = (UCHAR)(m_Height >> 8) & 0xff;
sg_BHeader[0x18] = (UCHAR)(m_Height >> 16) & 0xff;
sg_BHeader[0x19] = (UCHAR)(m_Height >> 24) & 0xff;
write(bmp, sg_BHeader, sizeof(sg_BHeader));
for(i = 0; i < m_Height; i++) {
unsigned long *c = p + (m_Height - 1 - i) * m_Width;
unsigned long cc;
for(j = 0; j < m_Width; j++) {
cc = *(c + j);
//cc = *(c + j);
write(bmp, &cc, 3);
}
}
close(bmp);
}
void performReadSpeedTest(void *fb, int fbSize) {
int i, j, run;
struct timeval startTime, endTime;
unsigned long long results[PERFORMANCE_RUN_COUNT];
unsigned long long average;
unsigned char *testImage;
printf("Frame Buffer Performance read test...\n");
for (run = 0; run < PERFORMANCE_RUN_COUNT; ++run) {
testImage = (unsigned char *) malloc(fbSize);
gettimeofday(&startTime, NULL);
memcpy(testImage, fb, fbSize);
gettimeofday(&endTime, NULL);
long secsDiff = endTime.tv_sec - startTime.tv_sec;
results[run] = secsDiff * 1000000
+ (endTime.tv_usec - startTime.tv_usec);
}
average = 0;
for (i = 0; i < PERFORMANCE_RUN_COUNT; ++i)
average += results[i];
average = average / PERFORMANCE_RUN_COUNT;
printf(" Average: %llu usecs\n", average);
printf(" Bandwidth: %.03f MByte/Sec\n",
(fbSize / 1048576.0) / ((double) average / 1000000.0));
printf(" Max. FPS: %.03f fps\n\n", 1000000.0 / (double) average);
SaveBMPFile(testImage, "./fb_bmp.bmp");
free(testImage);
memset(fb, 0, fbSize);
}
int WriteBMPtoFB() {
FILE *fp;
PIXEL pix;
int rc;
const char *filename = BMP_PIC_DIR;
unsigned int line_x = 0, line_y = 0;
unsigned long tmp, location = 0, BytesPerLine = 0;
unsigned char *imageBuffer;
fp = fopen(filename, "rb" );
if (fp == NULL) {
printf("no picture!\n");
exit(1);
}
rc = fread( &FileHead, sizeof(BITMAPFILEHEADER),1, fp );
if ( rc != 1) {
printf("read header error!\n");
fclose( fp );
exit(1);
}
if (memcmp(FileHead.cfType, "BM", 2) != 0) {
printf("it's not a BMP file\n");
fclose( fp );
exit(1);
}
rc = fread( (char *)&InfoHead, sizeof(BITMAPINFOHEADER),1, fp );
if ( rc != 1) {
printf("read infoheader error!\n");
fclose( fp );
exit(1);
}
//跳转的数据区
fseek(fp, FileHead.cfoffBits, SEEK_SET);
printf("FileHead.cfoffBits = %ld\n",FileHead.cfoffBits);
//向framebuffer中写BMP图片
imageBuffer = (unsigned char *) malloc(screensize);
while(!feof(fp)) {
rc = fread( (char *)&pix, 1, 3, fp);
location = line_x * bits_per_pixel / 8 + (InfoHead.ciHeight - line_y - 1) * xres * bits_per_pixel / 8;
//将每个像素填写到imageBuffer中
imageBuffer[location + 3] = 0xff;
imageBuffer[location + 2] = (pix.red & 0xff);
imageBuffer[location + 1] = (pix.green & 0xff);
imageBuffer[location + 0] = (pix.blue & 0xff);
line_x++;
if (line_x == InfoHead.ciWidth ) {
line_x = 0;
line_y++;
if(line_y == InfoHead.ciHeight) {
break;
}
}
}
memcpy(frameBuffer, imageBuffer, screensize);
//memset(frameBuffer, 0, screensize);
fclose(fp);
return(0);
}
int main() {
const char *dev = FBDEV;
int fd = 0;
fd = open(dev, O_RDWR);
if (fd == -1) {
printf("Error: cannot open framebuffer device");
exit(1);
}
//获取finfo信息并显示
if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1) {
printf("Error reading fixed information");
exit(1);
}
printFixedInfo();
//获取vinfo信息并显示
if (ioctl(fd, FBIOGET_VSCREENINFO, &vinfo) == -1) {
printf("Error reading variable information");
exit(1);
}
printVariableInfo();
//屏幕总的字节长度
screensize = finfo.smem_len;
xres = vinfo.xres;
yres = vinfo.yres;
bits_per_pixel = vinfo.bits_per_pixel;
//内存映射
frameBuffer = (char *) mmap(0, screensize, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
if (frameBuffer == MAP_FAILED) {
printf("Error: Failed to map framebuffer device to memory");
exit(1);
}
//测试virt fb的性能
memset(frameBuffer, 0, screensize);
performWriteSpeedTest(frameBuffer, screensize);
performReadSpeedTest(frameBuffer, screensize);
printf("Write a 32bpp BMP image to Fb!\n");
WriteBMPtoFB();
printf(" Done.\n");
//memset(frameBuffer, 0, screensize);
//munmap(frameBuffer, screensize); //解除内存映射,与mmap对应
close(fd);
exit(0);
}