/* * Test the GLFBDev interface. Only tested with radeonfb driver!!!! * * Written by Brian Paul */ #include <assert.h> #include <errno.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <sys/types.h> #include <linux/fb.h> #include <linux/kd.h> #include <linux/vt.h> #include <GL/gl.h> #include <GL/glfbdev.h> #include <math.h> /** * Choose one of these modes */ /*static const int XRes = 1280, YRes = 1024, Hz = 75;*/ /*static const int XRes = 1280, YRes = 1024, Hz = 70;*/ /*static const int XRes = 1280, YRes = 1024, Hz = 60;*/ static const int XRes = 1024, YRes = 768, Hz = 70; static int DesiredDepth = 32; static int NumFrames = 100; static struct fb_fix_screeninfo FixedInfo; static struct fb_var_screeninfo VarInfo, OrigVarInfo; static int OriginalVT = -1; static int ConsoleFD = -1; static int FrameBufferFD = -1; static caddr_t FrameBuffer = (caddr_t) -1; static caddr_t MMIOAddress = (caddr_t) -1; static void print_fixed_info(const struct fb_fix_screeninfo *fixed, const char *s) { static const char *visuals[] = { "MONO01", "MONO10", "TRUECOLOR", "PSEUDOCOLOR", "DIRECTCOLOR", "STATIC_PSEUDOCOLOR" }; printf("%s info -----------------------\n", s); printf("id = %16s\n", fixed->id); printf("smem_start = 0x%lx\n", fixed->smem_start); printf("smem_len = %d (0x%x)\n", fixed->smem_len, fixed->smem_len); printf("type = 0x%x\n", fixed->type); printf("type_aux = 0x%x\n", fixed->type_aux); printf("visual = 0x%x (%s)\n", fixed->visual, visuals[fixed->visual]); printf("xpanstep = %d\n", fixed->xpanstep); printf("ypanstep = %d\n", fixed->ypanstep); printf("ywrapstep = %d\n", fixed->ywrapstep); printf("line_length = %d\n", fixed->line_length); printf("mmio_start = 0x%lx\n", fixed->mmio_start); printf("mmio_len = %d (0x%x)\n", fixed->mmio_len, fixed->mmio_len); printf("accel = 0x%x\n", fixed->accel); } static void print_var_info(const struct fb_var_screeninfo *var, const char *s) { printf("%s info -----------------------\n", s); printf("xres = %d\n", var->xres); printf("yres = %d\n", var->yres); printf("xres_virtual = %d\n", var->xres_virtual); printf("yres_virtual = %d\n", var->yres_virtual); printf("xoffset = %d\n", var->xoffset); printf("yoffset = %d\n", var->yoffset); printf("bits_per_pixel = %d\n", var->bits_per_pixel); printf("grayscale = %d\n", var->grayscale); printf("red.offset = %d length = %d msb_right = %d\n", var->red.offset, var->red.length, var->red.msb_right); printf("green.offset = %d length = %d msb_right = %d\n", var->green.offset, var->green.length, var->green.msb_right); printf("blue.offset = %d length = %d msb_right = %d\n", var->blue.offset, var->blue.length, var->blue.msb_right); printf("transp.offset = %d length = %d msb_right = %d\n", var->transp.offset, var->transp.length, var->transp.msb_right); printf("nonstd = %d\n", var->nonstd); printf("activate = %d\n", var->activate); printf("height = %d mm\n", var->height); printf("width = %d mm\n", var->width); printf("accel_flags = 0x%x\n", var->accel_flags); printf("pixclock = %d\n", var->pixclock); printf("left_margin = %d\n", var->left_margin); printf("right_margin = %d\n", var->right_margin); printf("upper_margin = %d\n", var->upper_margin); printf("lower_margin = %d\n", var->lower_margin); printf("hsync_len = %d\n", var->hsync_len); printf("vsync_len = %d\n", var->vsync_len); printf("sync = %d\n", var->sync); printf("vmode = %d\n", var->vmode); } static void signal_handler(int signumber) { signal(signumber, SIG_IGN); /* prevent recursion! */ fprintf(stderr, "error: got signal %d (exiting)\n", signumber); exit(1); } static void initialize_fbdev( void ) { char ttystr[1000]; int fd, vtnumber, ttyfd; int sz; (void) sz; if (geteuid()) { fprintf(stderr, "error: you need to be root\n"); exit(1); } #if 1 /* open the framebuffer device */ FrameBufferFD = open("/dev/fb0", O_RDWR); if (FrameBufferFD < 0) { fprintf(stderr, "Error opening /dev/fb0: %s\n", strerror(errno)); exit(1); } #endif /* open /dev/tty0 and get the vt number */ if ((fd = open("/dev/tty0", O_WRONLY, 0)) < 0) { fprintf(stderr, "error opening /dev/tty0\n"); exit(1); } if (ioctl(fd, VT_OPENQRY, &vtnumber) < 0 || vtnumber < 0) { fprintf(stderr, "error: couldn't get a free vt\n"); exit(1); } close(fd); /* open the console tty */ sprintf(ttystr, "/dev/tty%d", vtnumber); /* /dev/tty1-64 */ ConsoleFD = open(ttystr, O_RDWR | O_NDELAY, 0); if (ConsoleFD < 0) { fprintf(stderr, "error couldn't open console fd\n"); exit(1); } /* save current vt number */ { struct vt_stat vts; if (ioctl(ConsoleFD, VT_GETSTATE, &vts) == 0) OriginalVT = vts.v_active; } /* disconnect from controlling tty */ ttyfd = open("/dev/tty", O_RDWR); if (ttyfd >= 0) { ioctl(ttyfd, TIOCNOTTY, 0); close(ttyfd); } /* some magic to restore the vt when we exit */ { struct vt_mode vt; if (ioctl(ConsoleFD, VT_ACTIVATE, vtnumber) != 0) printf("ioctl VT_ACTIVATE: %s\n", strerror(errno)); if (ioctl(ConsoleFD, VT_WAITACTIVE, vtnumber) != 0) printf("ioctl VT_WAITACTIVE: %s\n", strerror(errno)); if (ioctl(ConsoleFD, VT_GETMODE, &vt) < 0) { fprintf(stderr, "error: ioctl VT_GETMODE: %s\n", strerror(errno)); exit(1); } vt.mode = VT_PROCESS; vt.relsig = SIGUSR1; vt.acqsig = SIGUSR1; if (ioctl(ConsoleFD, VT_SETMODE, &vt) < 0) { fprintf(stderr, "error: ioctl(VT_SETMODE) failed: %s\n", strerror(errno)); exit(1); } } /* go into graphics mode */ if (ioctl(ConsoleFD, KDSETMODE, KD_GRAPHICS) < 0) { fprintf(stderr, "error: ioctl(KDSETMODE, KD_GRAPHICS) failed: %s\n", strerror(errno)); exit(1); } #if 0 /* open the framebuffer device */ FrameBufferFD = open("/dev/fb0", O_RDWR); if (FrameBufferFD < 0) { fprintf(stderr, "Error opening /dev/fb0: %s\n", strerror(errno)); exit(1); } #endif /* Get the fixed screen info */ if (ioctl(FrameBufferFD, FBIOGET_FSCREENINFO, &FixedInfo)) { fprintf(stderr, "error: ioctl(FBIOGET_FSCREENINFO) failed: %s\n", strerror(errno)); exit(1); } print_fixed_info(&FixedInfo, "Fixed"); /* get the variable screen info */ if (ioctl(FrameBufferFD, FBIOGET_VSCREENINFO, &OrigVarInfo)) { fprintf(stderr, "error: ioctl(FBIOGET_VSCREENINFO) failed: %s\n", strerror(errno)); exit(1); } print_var_info(&OrigVarInfo, "Orig Var"); /* operate on a copy */ VarInfo = OrigVarInfo; /* set the depth, resolution, etc */ if (DesiredDepth) VarInfo.bits_per_pixel = DesiredDepth; if (VarInfo.bits_per_pixel == 16) { VarInfo.red.offset = 11; VarInfo.green.offset = 5; VarInfo.blue.offset = 0; VarInfo.red.length = 5; VarInfo.green.length = 6; VarInfo.blue.length = 5; VarInfo.transp.offset = 0; VarInfo.transp.length = 0; } else if (VarInfo.bits_per_pixel == 32) { VarInfo.red.offset = 16; VarInfo.green.offset = 8; VarInfo.blue.offset = 0; VarInfo.transp.offset = 24; VarInfo.red.length = 8; VarInfo.green.length = 8; VarInfo.blue.length = 8; VarInfo.transp.length = 8; } /* timing values taken from /etc/fb.modes */ if (XRes == 1280 && YRes == 1024) { VarInfo.xres_virtual = VarInfo.xres = XRes; VarInfo.yres_virtual = VarInfo.yres = YRes; if (Hz == 75) { VarInfo.pixclock = 7408; VarInfo.left_margin = 248; VarInfo.right_margin = 16; VarInfo.upper_margin = 38; VarInfo.lower_margin = 1; VarInfo.hsync_len = 144; VarInfo.vsync_len = 3; } else if (Hz == 70) { VarInfo.pixclock = 7937; VarInfo.left_margin = 216; VarInfo.right_margin = 80; VarInfo.upper_margin = 36; VarInfo.lower_margin = 1; VarInfo.hsync_len = 112; VarInfo.vsync_len = 5; } else if (Hz == 60) { VarInfo.pixclock = 9260; VarInfo.left_margin = 248; VarInfo.right_margin = 48; VarInfo.upper_margin = 38; VarInfo.lower_margin = 1; VarInfo.hsync_len = 112; VarInfo.vsync_len = 3; } else { fprintf(stderr, "invalid rate for 1280x1024\n"); exit(1); } } else if (XRes == 1024 && YRes == 768 && Hz == 70) { VarInfo.xres_virtual = VarInfo.xres = XRes; VarInfo.yres_virtual = VarInfo.yres = YRes; if (Hz == 70) { VarInfo.pixclock = 13334; VarInfo.left_margin = 144; VarInfo.right_margin = 24; VarInfo.upper_margin = 29; VarInfo.lower_margin = 3; VarInfo.hsync_len = 136; VarInfo.vsync_len = 6; } else { fprintf(stderr, "invalid rate for 1024x768\n"); exit(1); } } VarInfo.xoffset = 0; VarInfo.yoffset = 0; VarInfo.nonstd = 0; VarInfo.vmode &= ~FB_VMODE_YWRAP; /* turn off scrolling */ /* set new variable screen info */ if (ioctl(FrameBufferFD, FBIOPUT_VSCREENINFO, &VarInfo)) { fprintf(stderr, "ioctl(FBIOPUT_VSCREENINFO failed): %s\n", strerror(errno)); exit(1); } print_var_info(&VarInfo, "New Var"); if (FixedInfo.visual != FB_VISUAL_TRUECOLOR && FixedInfo.visual != FB_VISUAL_DIRECTCOLOR) { fprintf(stderr, "non-TRUE/DIRECT-COLOR visuals (0x%x) not supported by this demo.\n", FixedInfo.visual); exit(1); } /* initialize colormap */ if (FixedInfo.visual == FB_VISUAL_DIRECTCOLOR) { struct fb_cmap cmap; unsigned short red[256], green[256], blue[256]; int i; /* we're assuming 256 entries here */ printf("initializing directcolor colormap\n"); cmap.start = 0; cmap.len = 256; cmap.red = red; cmap.green = green; cmap.blue = blue; cmap.transp = NULL; for (i = 0; i < cmap.len; i++) { red[i] = green[i] = blue[i] = (i << 8) | i; } if (ioctl(FrameBufferFD, FBIOPUTCMAP, (void *) &cmap) < 0) { fprintf(stderr, "ioctl(FBIOPUTCMAP) failed [%d]\n", i); } } /* * fbdev says the frame buffer is at offset zero, and the mmio region * is immediately after. */ /* mmap the framebuffer into our address space */ FrameBuffer = (caddr_t) mmap(0, /* start */ FixedInfo.smem_len, /* bytes */ PROT_READ | PROT_WRITE, /* prot */ MAP_SHARED, /* flags */ FrameBufferFD, /* fd */ 0 /* offset */); if (FrameBuffer == (caddr_t) - 1) { fprintf(stderr, "error: unable to mmap framebuffer: %s\n", strerror(errno)); exit(1); } printf("FrameBuffer = %p\n", FrameBuffer); #if 1 /* mmap the MMIO region into our address space */ MMIOAddress = (caddr_t) mmap(0, /* start */ FixedInfo.mmio_len, /* bytes */ PROT_READ | PROT_WRITE, /* prot */ MAP_SHARED, /* flags */ FrameBufferFD, /* fd */ FixedInfo.smem_len /* offset */); if (MMIOAddress == (caddr_t) - 1) { fprintf(stderr, "error: unable to mmap mmio region: %s\n", strerror(errno)); } printf("MMIOAddress = %p\n", MMIOAddress); /* try out some simple MMIO register reads */ if (0) { typedef unsigned int CARD32; typedef unsigned char CARD8; #define RADEON_CONFIG_MEMSIZE 0x00f8 #define RADEON_MEM_SDRAM_MODE_REG 0x0158 #define MMIO_IN32(base, offset) \ *(volatile CARD32 *)(void *)(((CARD8*)(base)) + (offset)) #define INREG(addr) MMIO_IN32(MMIOAddress, addr) int sz, type; const char *typeStr[] = {"SDR", "DDR", "64-bit SDR"}; sz = INREG(RADEON_CONFIG_MEMSIZE); type = INREG(RADEON_MEM_SDRAM_MODE_REG); printf("RADEON_CONFIG_MEMSIZE = %d (%d MB)\n", sz, sz / 1024 / 1024); printf("RADEON_MEM_SDRAM_MODE_REG >> 30 = %d (%s)\n", type >> 30, typeStr[type>>30]); } #endif } static void shutdown_fbdev( void ) { struct vt_mode VT; printf("cleaning up...\n"); /* restore original variable screen info */ if (ioctl(FrameBufferFD, FBIOPUT_VSCREENINFO, &OrigVarInfo)) { fprintf(stderr, "ioctl(FBIOPUT_VSCREENINFO failed): %s\n", strerror(errno)); exit(1); } munmap(MMIOAddress, FixedInfo.mmio_len); munmap(FrameBuffer, FixedInfo.smem_len); close(FrameBufferFD); /* restore text mode */ ioctl(ConsoleFD, KDSETMODE, KD_TEXT); /* set vt */ if (ioctl(ConsoleFD, VT_GETMODE, &VT) != -1) { VT.mode = VT_AUTO; ioctl(ConsoleFD, VT_SETMODE, &VT); } /* restore original vt */ if (OriginalVT >= 0) { ioctl(ConsoleFD, VT_ACTIVATE, OriginalVT); OriginalVT = -1; } close(ConsoleFD); } /* Borrowed from GLUT */ static void doughnut(GLfloat r, GLfloat R, GLint nsides, GLint rings) { int i, j; GLfloat theta, phi, theta1; GLfloat cosTheta, sinTheta; GLfloat cosTheta1, sinTheta1; GLfloat ringDelta, sideDelta; ringDelta = 2.0 * M_PI / rings; sideDelta = 2.0 * M_PI / nsides; theta = 0.0; cosTheta = 1.0; sinTheta = 0.0; for (i = rings - 1; i >= 0; i--) { theta1 = theta + ringDelta; cosTheta1 = cos(theta1); sinTheta1 = sin(theta1); glBegin(GL_QUAD_STRIP); phi = 0.0; for (j = nsides; j >= 0; j--) { GLfloat cosPhi, sinPhi, dist; phi += sideDelta; cosPhi = cos(phi); sinPhi = sin(phi); dist = R + r * cosPhi; glNormal3f(cosTheta1 * cosPhi, -sinTheta1 * cosPhi, sinPhi); glVertex3f(cosTheta1 * dist, -sinTheta1 * dist, r * sinPhi); glNormal3f(cosTheta * cosPhi, -sinTheta * cosPhi, sinPhi); glVertex3f(cosTheta * dist, -sinTheta * dist, r * sinPhi); } glEnd(); theta = theta1; cosTheta = cosTheta1; sinTheta = sinTheta1; } } static void gltest( void ) { static const int attribs[] = { GLFBDEV_DOUBLE_BUFFER, GLFBDEV_DEPTH_SIZE, 16, GLFBDEV_NONE }; GLFBDevContextPtr ctx; GLFBDevBufferPtr buf; GLFBDevVisualPtr vis; int bytes, r, g, b, a; float ang; int i; printf("GLFBDEV_VENDOR = %s\n", glFBDevGetString(GLFBDEV_VENDOR)); printf("GLFBDEV_VERSION = %s\n", glFBDevGetString(GLFBDEV_VERSION)); /* framebuffer size */ bytes = VarInfo.xres_virtual * VarInfo.yres_virtual * VarInfo.bits_per_pixel / 8; vis = glFBDevCreateVisual( &FixedInfo, &VarInfo, attribs ); assert(vis); buf = glFBDevCreateBuffer( &FixedInfo, &VarInfo, vis, FrameBuffer, NULL, bytes ); assert(buf); ctx = glFBDevCreateContext( vis, NULL ); assert(buf); b = glFBDevMakeCurrent( ctx, buf, buf ); assert(b); /*printf("GL_EXTENSIONS: %s\n", glGetString(GL_EXTENSIONS));*/ glGetIntegerv(GL_RED_BITS, &r); glGetIntegerv(GL_GREEN_BITS, &g); glGetIntegerv(GL_BLUE_BITS, &b); glGetIntegerv(GL_ALPHA_BITS, &a); printf("RED_BITS=%d GREEN_BITS=%d BLUE_BITS=%d ALPHA_BITS=%d\n", r, g, b, a); glClearColor(0.5, 0.5, 1.0, 0); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(-1, 1, -1, 1, 2, 30); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0, 0, -15); glViewport(0, 0, VarInfo.xres_virtual, VarInfo.yres_virtual); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_DEPTH_TEST); printf("Drawing %d frames...\n", NumFrames); ang = 0.0; for (i = 0; i < NumFrames; i++) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix(); glRotatef(ang, 1, 0, 0); doughnut(1, 3, 40, 20); glPopMatrix(); glFBDevSwapBuffers(buf); ang += 15.0; } /* clean up */ b = glFBDevMakeCurrent( NULL, NULL, NULL); assert(b); glFBDevDestroyContext(ctx); glFBDevDestroyBuffer(buf); glFBDevDestroyVisual(vis); } static void parse_args(int argc, char *argv[]) { int i; for (i = 1; i < argc; i++) { if (strcmp(argv[i], "-f") == 0) { NumFrames = atoi(argv[i+1]); i++; } } } int main( int argc, char *argv[] ) { signal(SIGUSR1, signal_handler); /* exit if someone tries a vt switch */ signal(SIGSEGV, signal_handler); /* catch segfaults */ parse_args(argc, argv); printf("Setting mode to %d x %d @ %d Hz, %d bpp\n", XRes, YRes, Hz, DesiredDepth); initialize_fbdev(); gltest(); shutdown_fbdev(); return 0; }