glFBDev driver from embedded-2 branch.
authorBrian Paul <brian.paul@tungstengraphics.com>
Sat, 6 Dec 2003 17:20:10 +0000 (17:20 +0000)
committerBrian Paul <brian.paul@tungstengraphics.com>
Sat, 6 Dec 2003 17:20:10 +0000 (17:20 +0000)
Probably won't be actively used/maintained, but bring it to the trunk so
it doesn't get lost.

include/GL/glfbdev.h [new file with mode: 0644]
progs/fbdev/glfbdevtest.c [new file with mode: 0644]
src/mesa/drivers/fbdev/glfbdev.c [new file with mode: 0644]

diff --git a/include/GL/glfbdev.h b/include/GL/glfbdev.h
new file mode 100644 (file)
index 0000000..cd6cdd5
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Mesa 3-D graphics library
+ * Version:  5.1
+ *
+ * Copyright (C) 1999-2003  Brian Paul   All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+#ifndef GLFBDEV_H
+#define GLFBDEV_H
+
+
+/* avoid including linux/fb.h */
+struct fb_fix_screeninfo;
+struct fb_var_screeninfo;
+
+
+/* public types */
+typedef struct GLFBDevVisualRec *GLFBDevVisualPtr;
+typedef struct GLFBDevBufferRec *GLFBDevBufferPtr;
+typedef struct GLFBDevContextRec *GLFBDevContextPtr;
+
+
+/* API version */
+#define GLFBDEV_VERSION_1_0       1
+
+
+/* For glFBDevCreateVisual */
+#define GLFBDEV_DOUBLE_BUFFER   100
+#define GLFBDEV_COLOR_INDEX     101
+#define GLFBDEV_DEPTH_SIZE      102
+#define GLFBDEV_STENCIL_SIZE    103
+#define GLFBDEV_ACCUM_SIZE      104
+#define GLFBDEV_LEVEL           105
+#define GLFBDEV_NONE              0
+
+/* For glFBDevGetString */
+#define GLFBDEV_VERSION         200
+#define GLFBDEV_VENDOR          201
+
+
+/* Misc functions */
+
+extern const char *
+glFBDevGetString( int str );
+
+
+extern const void *
+glFBDevGetProcAddress( const char *procName );
+
+
+
+/**
+ * Create a GLFBDevVisual.
+ * \param fixInfo - needed to get the visual types, etc.
+ * \param varInfo - needed to get the bits_per_pixel, etc.
+ * \param attribs - for requesting depth, stencil, accum buffers, etc.
+ */
+extern GLFBDevVisualPtr
+glFBDevCreateVisual( const struct fb_fix_screeninfo *fixInfo,
+                     const struct fb_var_screeninfo *varInfo,
+                     const int *attribs );
+
+extern void
+glFBDevDestroyVisual( GLFBDevVisualPtr visual );
+
+extern int
+glFBDevGetVisualAttrib( const GLFBDevVisualPtr visual, int attrib);
+
+
+
+/**
+ * Create a GLFBDevBuffer.
+ * \param fixInfo, varInfo - needed in order to get the screen size
+ * (resolution), etc.
+ * \param visual - as returned by glFBDevCreateVisual()
+ * \param frontBuffer - address of front color buffer
+ * \param backBuffer - address of back color buffer (may be NULL)
+ * \param size - size of the color buffer(s) in bytes.
+ */
+extern GLFBDevBufferPtr
+glFBDevCreateBuffer( const struct fb_fix_screeninfo *fixInfo,
+                     const struct fb_var_screeninfo *varInfo,
+                     const GLFBDevVisualPtr visual,
+                     void *frontBuffer, void *backBuffer, size_t size );
+
+extern void
+glFBDevDestroyBuffer( GLFBDevBufferPtr buffer );
+
+extern int
+glFBDevGetBufferAttrib( const GLFBDevBufferPtr buffer, int attrib);
+
+extern GLFBDevBufferPtr
+glFBDevGetCurrentDrawBuffer( void );
+
+extern GLFBDevBufferPtr
+glFBDevGetCurrentReadBuffer( void );
+
+extern void
+glFBDevSwapBuffers( GLFBDevBufferPtr buffer );
+
+
+
+/**
+ * Create a GLFBDevContext.
+ * \param visual - as created by glFBDevCreateVisual.
+ * \param share - specifies another context with which to share textures,
+ * display lists, etc. (may be NULL).
+ */
+extern GLFBDevContextPtr
+glFBDevCreateContext( const GLFBDevVisualPtr visual, GLFBDevContextPtr share );
+
+extern void
+glFBDevDestroyContext( GLFBDevContextPtr context );
+
+extern int
+glFBDevGetContextAttrib( const GLFBDevContextPtr context, int attrib);
+
+extern GLFBDevContextPtr
+glFBDevGetCurrentContext( void );
+
+extern int
+glFBDevMakeCurrent( GLFBDevContextPtr context,
+                    GLFBDevBufferPtr drawBuffer,
+                    GLFBDevBufferPtr readBuffer );
+
+
+#endif /* GLFBDEV_H */
diff --git a/progs/fbdev/glfbdevtest.c b/progs/fbdev/glfbdevtest.c
new file mode 100644 (file)
index 0000000..a461c55
--- /dev/null
@@ -0,0 +1,524 @@
+/*
+ * 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>
+
+#define DEFAULT_DEPTH 8
+
+static struct fb_fix_screeninfo FixedInfo;
+static struct fb_var_screeninfo VarInfo, OrigVarInfo;
+static int DesiredDepth = 0;
+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 */
+   DesiredDepth = 32;
+   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 (1280x1024 @ 75Hz) */
+   VarInfo.xres_virtual = VarInfo.xres = 1280;
+   VarInfo.yres_virtual = VarInfo.yres = 1024;
+   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;
+
+   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 (1)
+   {
+      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;
+
+   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);
+
+   for (ang = 0; ang <= 180; ang += 15) {
+      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+      glPushMatrix();
+      glRotatef(ang, 1, 0, 0);
+      doughnut(1, 3, 40, 20);
+      glPopMatrix();
+      glFBDevSwapBuffers(buf);
+   }
+
+   /* clean up */
+   b = glFBDevMakeCurrent( NULL, NULL, NULL);
+   assert(b);
+
+   glFBDevDestroyContext(ctx);
+   glFBDevDestroyBuffer(buf);
+   glFBDevDestroyVisual(vis);
+}
+
+
+int
+main( int argc, char *argv[] )
+{
+   signal(SIGUSR1, signal_handler);  /* exit if someone tries a vt switch */
+   signal(SIGSEGV, signal_handler);  /* catch segfaults */
+
+   initialize_fbdev();
+   gltest();
+   shutdown_fbdev();
+
+   return 0;
+}
diff --git a/src/mesa/drivers/fbdev/glfbdev.c b/src/mesa/drivers/fbdev/glfbdev.c
new file mode 100644 (file)
index 0000000..6d3ed01
--- /dev/null
@@ -0,0 +1,828 @@
+/*
+ * Mesa 3-D graphics library
+ * Version:  5.1
+ *
+ * Copyright (C) 1999-2003  Brian Paul   All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+/*
+ * OpenGL (Mesa) interface for fbdev.
+ * For info about fbdev:
+ * http://www.tldp.org/HOWTO/Framebuffer-HOWTO.html
+ *
+ * known VGA modes
+ * Colours   640x400 640x480 800x600 1024x768 1152x864 1280x1024 1600x1200
+ * --------+--------------------------------------------------------------
+ *  4 bits |    ?       ?     0x302      ?        ?        ?         ?
+ *  8 bits |  0x300   0x301   0x303    0x305    0x161    0x307     0x31C
+ * 15 bits |    ?     0x310   0x313    0x316    0x162    0x319     0x31D
+ * 16 bits |    ?     0x311   0x314    0x317    0x163    0x31A     0x31E
+ * 24 bits |    ?     0x312   0x315    0x318      ?      0x31B     0x31F
+ * 32 bits |    ?       ?       ?        ?      0x164      ?
+ */
+
+
+#ifdef USE_GLFBDEV_DRIVER
+
+#include "glheader.h"
+#include <linux/fb.h>
+#include "GL/glfbdev.h"
+#include "context.h"
+#include "extensions.h"
+#include "imports.h"
+#include "texformat.h"
+#include "teximage.h"
+#include "texstore.h"
+#include "array_cache/acache.h"
+#include "swrast/swrast.h"
+#include "swrast_setup/swrast_setup.h"
+#include "tnl/tnl.h"
+#include "tnl/t_context.h"
+#include "tnl/t_pipeline.h"
+
+
+#define PF_B8G8R8     1
+#define PF_B8G8R8A8   2
+#define PF_B5G6R5     3
+#define PF_B5G5R5     4
+#define PF_CI8        5
+
+
+/*
+ * Derived from Mesa's GLvisual class.
+ */
+struct GLFBDevVisualRec {
+   GLvisual glvisual;              /* base class */
+   struct fb_fix_screeninfo fix;
+   struct fb_var_screeninfo var;
+   int pixelFormat;
+};
+
+/*
+ * Derived from Mesa's GLframebuffer class.
+ */
+struct GLFBDevBufferRec {
+   GLframebuffer glframebuffer;    /* base class */
+   GLFBDevVisualPtr visual;
+   struct fb_fix_screeninfo fix;
+   struct fb_var_screeninfo var;
+   void *frontStart;
+   void *backStart;
+   size_t size;
+   GLuint bytesPerPixel;
+   GLuint rowStride;               /* in bytes */
+   GLubyte *frontBottom;           /* pointer to last row */
+   GLubyte *backBottom;            /* pointer to last row */
+   GLubyte *curBottom;             /* = frontBottom or backBottom */
+   GLboolean mallocBackBuffer;
+};
+
+/*
+ * Derived from Mesa's GLcontext class.
+ */
+struct GLFBDevContextRec {
+   GLcontext glcontext;            /* base class */
+   GLFBDevVisualPtr visual;
+   GLFBDevBufferPtr drawBuffer;
+   GLFBDevBufferPtr readBuffer;
+   GLFBDevBufferPtr curBuffer;
+};
+
+
+
+#define GLFBDEV_CONTEXT(CTX)  ((GLFBDevContextPtr) (CTX))
+#define GLFBDEV_BUFFER(BUF)  ((GLFBDevBufferPtr) (BUF))
+
+
+/**********************************************************************/
+/* Internal device driver functions                                   */
+/**********************************************************************/
+
+
+static const GLubyte *
+get_string(GLcontext *ctx, GLenum pname)
+{
+   (void) ctx;
+   switch (pname) {
+      case GL_RENDERER:
+         return (const GLubyte *) "Mesa glfbdev";
+      default:
+         return NULL;
+   }
+}
+
+
+static void
+update_state( GLcontext *ctx, GLuint new_state )
+{
+   /* not much to do here - pass it on */
+   _swrast_InvalidateState( ctx, new_state );
+   _swsetup_InvalidateState( ctx, new_state );
+   _ac_InvalidateState( ctx, new_state );
+   _tnl_InvalidateState( ctx, new_state );
+}
+
+
+static void
+get_buffer_size( GLframebuffer *buffer, GLuint *width, GLuint *height )
+{
+   const GLFBDevBufferPtr fbdevbuffer = (GLFBDevBufferPtr) buffer;
+   *width = fbdevbuffer->var.xres_virtual;
+   *height = fbdevbuffer->var.yres_virtual;
+}
+
+
+/* specifies the buffer for swrast span rendering/reading */
+static void
+set_buffer( GLcontext *ctx, GLframebuffer *buffer, GLuint bufferBit )
+{
+   GLFBDevContextPtr fbdevctx = GLFBDEV_CONTEXT(ctx);
+   GLFBDevBufferPtr fbdevbuf = GLFBDEV_BUFFER(buffer);
+   fbdevctx->curBuffer = fbdevbuf;
+   switch (bufferBit) {
+   case FRONT_LEFT_BIT:
+      fbdevbuf->curBottom = fbdevbuf->frontBottom;
+      break;
+   case BACK_LEFT_BIT:
+      fbdevbuf->curBottom = fbdevbuf->backBottom;
+      break;
+   default:
+      _mesa_problem(ctx, "bad bufferBit in set_buffer()");
+   }
+}
+
+
+static void
+init_core_functions( GLcontext *ctx )
+{
+   ctx->Driver.GetString = get_string;
+   ctx->Driver.UpdateState = update_state;
+   ctx->Driver.ResizeBuffers = _swrast_alloc_buffers;
+   ctx->Driver.GetBufferSize = get_buffer_size;
+
+   ctx->Driver.Accum = _swrast_Accum;
+   ctx->Driver.Bitmap = _swrast_Bitmap;
+   ctx->Driver.Clear = _swrast_Clear;  /* would be good to optimize */
+   ctx->Driver.CopyPixels = _swrast_CopyPixels;
+   ctx->Driver.DrawPixels = _swrast_DrawPixels;
+   ctx->Driver.ReadPixels = _swrast_ReadPixels;
+   ctx->Driver.DrawBuffer = _swrast_DrawBuffer;
+
+   ctx->Driver.ChooseTextureFormat = _mesa_choose_tex_format;
+   ctx->Driver.TexImage1D = _mesa_store_teximage1d;
+   ctx->Driver.TexImage2D = _mesa_store_teximage2d;
+   ctx->Driver.TexImage3D = _mesa_store_teximage3d;
+   ctx->Driver.TexSubImage1D = _mesa_store_texsubimage1d;
+   ctx->Driver.TexSubImage2D = _mesa_store_texsubimage2d;
+   ctx->Driver.TexSubImage3D = _mesa_store_texsubimage3d;
+   ctx->Driver.TestProxyTexImage = _mesa_test_proxy_teximage;
+
+   ctx->Driver.CompressedTexImage1D = _mesa_store_compressed_teximage1d;
+   ctx->Driver.CompressedTexImage2D = _mesa_store_compressed_teximage2d;
+   ctx->Driver.CompressedTexImage3D = _mesa_store_compressed_teximage3d;
+   ctx->Driver.CompressedTexSubImage1D = _mesa_store_compressed_texsubimage1d;
+   ctx->Driver.CompressedTexSubImage2D = _mesa_store_compressed_texsubimage2d;
+   ctx->Driver.CompressedTexSubImage3D = _mesa_store_compressed_texsubimage3d;
+
+   ctx->Driver.CopyTexImage1D = _swrast_copy_teximage1d;
+   ctx->Driver.CopyTexImage2D = _swrast_copy_teximage2d;
+   ctx->Driver.CopyTexSubImage1D = _swrast_copy_texsubimage1d;
+   ctx->Driver.CopyTexSubImage2D = _swrast_copy_texsubimage2d;
+   ctx->Driver.CopyTexSubImage3D = _swrast_copy_texsubimage3d;
+   ctx->Driver.CopyColorTable = _swrast_CopyColorTable;
+   ctx->Driver.CopyColorSubTable = _swrast_CopyColorSubTable;
+   ctx->Driver.CopyConvolutionFilter1D = _swrast_CopyConvolutionFilter1D;
+   ctx->Driver.CopyConvolutionFilter2D = _swrast_CopyConvolutionFilter2D;
+}
+
+
+/*
+ * Generate code for span functions.
+ */
+
+/* 24-bit BGR */
+#define NAME(PREFIX) PREFIX##_B8G8R8
+#define SPAN_VARS \
+   const GLFBDevContextPtr fbdevctx = GLFBDEV_CONTEXT(ctx); \
+   const GLFBDevBufferPtr fbdevbuf = fbdevctx->curBuffer;
+#define INIT_PIXEL_PTR(P, X, Y) \
+   GLubyte *P = fbdevbuf->curBottom - (Y) * fbdevbuf->rowStride + (X) * 3
+#define INC_PIXEL_PTR(P) P += 3
+#define STORE_RGB_PIXEL(P, X, Y, R, G, B) \
+   P[0] = B;  P[1] = G;  P[2] = R
+#define STORE_RGBA_PIXEL(P, X, Y, R, G, B, A) \
+   P[0] = B;  P[1] = G;  P[2] = R
+#define FETCH_RGBA_PIXEL(R, G, B, A, P) \
+   R = P[2];  G = P[1];  B = P[0];  A = CHAN_MAX
+
+#include "swrast/s_spantemp.h"
+
+
+/* 32-bit BGRA */
+#define NAME(PREFIX) PREFIX##_B8G8R8A8
+#define SPAN_VARS \
+   const GLFBDevContextPtr fbdevctx = GLFBDEV_CONTEXT(ctx); \
+   const GLFBDevBufferPtr fbdevbuf = fbdevctx->curBuffer;
+#define INIT_PIXEL_PTR(P, X, Y) \
+   GLubyte *P = fbdevbuf->curBottom - (Y) * fbdevbuf->rowStride + (X) * 4
+#define INC_PIXEL_PTR(P) P += 4
+#define STORE_RGB_PIXEL(P, X, Y, R, G, B) \
+   P[0] = B;  P[1] = G;  P[2] = R;  P[3] = 255
+#define STORE_RGBA_PIXEL(P, X, Y, R, G, B, A) \
+   P[0] = B;  P[1] = G;  P[2] = R;  P[3] = A
+#define FETCH_RGBA_PIXEL(R, G, B, A, P) \
+   R = P[2];  G = P[1];  B = P[0];  A = P[3]
+
+#include "swrast/s_spantemp.h"
+
+
+/* 16-bit BGR (XXX implement dithering someday) */
+#define NAME(PREFIX) PREFIX##_B5G6R5
+#define SPAN_VARS \
+   const GLFBDevContextPtr fbdevctx = GLFBDEV_CONTEXT(ctx); \
+   const GLFBDevBufferPtr fbdevbuf = fbdevctx->curBuffer;
+#define INIT_PIXEL_PTR(P, X, Y) \
+   GLushort *P = (GLushort *) (fbdevbuf->curBottom - (Y) * fbdevbuf->rowStride + (X) * 2)
+#define INC_PIXEL_PTR(P) P += 1
+#define STORE_RGB_PIXEL(P, X, Y, R, G, B) \
+   *P = ( (((R) & 0xf8) << 8) | (((G) & 0xfc) << 3) | ((B) >> 3) )
+#define STORE_RGBA_PIXEL(P, X, Y, R, G, B, A) \
+   *P = ( (((R) & 0xf8) << 8) | (((G) & 0xfc) << 3) | ((B) >> 3) )
+#define FETCH_RGBA_PIXEL(R, G, B, A, P) \
+   R = ( (((*P) >> 8) & 0xf8) | (((*P) >> 11) & 0x7) ); \
+   G = ( (((*P) >> 3) & 0xfc) | (((*P) >>  5) & 0x3) ); \
+   B = ( (((*P) << 3) & 0xf8) | (((*P)      ) & 0x7) ); \
+   A = CHAN_MAX
+
+#include "swrast/s_spantemp.h"
+
+
+/* 15-bit BGR (XXX implement dithering someday) */
+#define NAME(PREFIX) PREFIX##_B5G5R5
+#define SPAN_VARS \
+   const GLFBDevContextPtr fbdevctx = GLFBDEV_CONTEXT(ctx); \
+   const GLFBDevBufferPtr fbdevbuf = fbdevctx->curBuffer;
+#define INIT_PIXEL_PTR(P, X, Y) \
+   GLushort *P = (GLushort *) (fbdevbuf->curBottom - (Y) * fbdevbuf->rowStride + (X) * 2)
+#define INC_PIXEL_PTR(P) P += 1
+#define STORE_RGB_PIXEL(P, X, Y, R, G, B) \
+   *P = ( (((R) & 0xf8) << 7) | (((G) & 0xf8) << 2) | ((B) >> 3) )
+#define STORE_RGBA_PIXEL(P, X, Y, R, G, B, A) \
+   *P = ( (((R) & 0xf8) << 7) | (((G) & 0xf8) << 2) | ((B) >> 3) )
+#define FETCH_RGBA_PIXEL(R, G, B, A, P) \
+   R = ( (((*P) >> 7) & 0xf8) | (((*P) >> 10) & 0x7) ); \
+   G = ( (((*P) >> 2) & 0xf8) | (((*P) >>  5) & 0x7) ); \
+   B = ( (((*P) << 3) & 0xf8) | (((*P)      ) & 0x7) ); \
+   A = CHAN_MAX
+
+#include "swrast/s_spantemp.h"
+
+
+/* 8-bit color index */
+#define NAME(PREFIX) PREFIX##_CI8
+#define SPAN_VARS \
+   const GLFBDevContextPtr fbdevctx = GLFBDEV_CONTEXT(ctx); \
+   const GLFBDevBufferPtr fbdevbuf = fbdevctx->curBuffer;
+#define INIT_PIXEL_PTR(P, X, Y) \
+   GLubyte *P = fbdevbuf->curBottom - (Y) * fbdevbuf->rowStride + (X)
+#define INC_PIXEL_PTR(P) P += 1
+#define STORE_CI_PIXEL(P, CI) \
+   P[0] = CI
+#define FETCH_CI_PIXEL(CI, P) \
+   CI = P[0]
+
+#include "swrast/s_spantemp.h"
+
+/**********************************************************************/
+/* Public API functions                                               */
+/**********************************************************************/
+
+
+const char *
+glFBDevGetString( int str )
+{
+   switch (str) {
+   case GLFBDEV_VENDOR:
+      return "Mesa Project";
+   case GLFBDEV_VERSION:
+      return "1.0.0";
+   default:
+      return NULL;
+   }
+}
+
+
+const void *
+glFBDevGetProcAddress( const char *procName )
+{
+   struct name_address {
+      const char *name;
+      const void *func;
+   };
+   static const struct name_address functions[] = {
+      { "glFBDevGetString", (void *) glFBDevGetString },
+      { "glFBDevGetProcAddress", (void *) glFBDevGetProcAddress },
+      { "glFBDevCreateVisual", (void *) glFBDevCreateVisual },
+      { "glFBDevDestroyVisual", (void *) glFBDevDestroyVisual },
+      { "glFBDevGetVisualAttrib", (void *) glFBDevGetVisualAttrib },
+      { "glFBDevCreateBuffer", (void *) glFBDevCreateBuffer },
+      { "glFBDevDestroyBuffer", (void *) glFBDevDestroyBuffer },
+      { "glFBDevGetBufferAttrib", (void *) glFBDevGetBufferAttrib },
+      { "glFBDevGetCurrentDrawBuffer", (void *) glFBDevGetCurrentDrawBuffer },
+      { "glFBDevGetCurrentReadBuffer", (void *) glFBDevGetCurrentReadBuffer },
+      { "glFBDevSwapBuffers", (void *) glFBDevSwapBuffers },
+      { "glFBDevCreateContext", (void *) glFBDevCreateContext },
+      { "glFBDevDestroyContext", (void *) glFBDevDestroyContext },
+      { "glFBDevGetContextAttrib", (void *) glFBDevGetContextAttrib },
+      { "glFBDevGetCurrentContext", (void *) glFBDevGetCurrentContext },
+      { "glFBDevMakeCurrent", (void *) glFBDevMakeCurrent },
+      { NULL, NULL }
+   };
+   const struct name_address *entry;
+   for (entry = functions; entry->name; entry++) {
+      if (_mesa_strcmp(entry->name, procName) == 0) {
+         return entry->func;
+      }
+   }
+   return _glapi_get_proc_address(procName);
+}
+
+
+GLFBDevVisualPtr
+glFBDevCreateVisual( const struct fb_fix_screeninfo *fixInfo,
+                     const struct fb_var_screeninfo *varInfo,
+                     const int *attribs )
+{
+   GLFBDevVisualPtr vis;
+   const int *attrib;
+   GLboolean rgbFlag = GL_TRUE, dbFlag = GL_FALSE, stereoFlag = GL_FALSE;
+   GLint redBits = 0, greenBits = 0, blueBits = 0, alphaBits = 0;
+   GLint indexBits = 0, depthBits = 0, stencilBits = 0;
+   GLint accumRedBits = 0, accumGreenBits = 0;
+   GLint accumBlueBits = 0, accumAlphaBits = 0;
+   GLint numSamples = 0;
+
+   ASSERT(fixInfo);
+   ASSERT(varInfo);
+
+   vis = CALLOC_STRUCT(GLFBDevVisualRec);
+   if (!vis)
+      return NULL;
+
+   vis->fix = *fixInfo;  /* struct assignment */
+   vis->var = *varInfo;  /* struct assignment */
+
+   for (attrib = attribs; attrib && *attrib != GLFBDEV_NONE; attrib++) {
+      switch (*attrib) {
+      case GLFBDEV_DOUBLE_BUFFER:
+         dbFlag = GL_TRUE;
+         break;
+      case GLFBDEV_COLOR_INDEX:
+         rgbFlag = GL_FALSE;
+         break;
+      case GLFBDEV_DEPTH_SIZE:
+         depthBits = attrib[1];
+         attrib++;
+         break;
+      case GLFBDEV_STENCIL_SIZE:
+         stencilBits = attrib[1];
+         attrib++;
+         break;
+      case GLFBDEV_ACCUM_SIZE:
+         accumRedBits = accumGreenBits = accumBlueBits = accumAlphaBits
+            = attrib[1];
+         attrib++;
+         break;
+      case GLFBDEV_LEVEL:
+         /* ignored for now */
+         break;
+      default:
+         /* unexpected token */
+         _mesa_free(vis);
+         return NULL;
+      }
+   }
+
+   if (rgbFlag) {
+      redBits   = varInfo->red.length;
+      greenBits = varInfo->green.length;
+      blueBits  = varInfo->blue.length;
+      alphaBits = varInfo->transp.length;
+
+      if ((fixInfo->visual == FB_VISUAL_TRUECOLOR ||
+           fixInfo->visual == FB_VISUAL_DIRECTCOLOR)
+          && varInfo->bits_per_pixel == 24
+          && varInfo->red.offset == 16
+          && varInfo->green.offset == 8
+          && varInfo->blue.offset == 0) {
+         vis->pixelFormat = PF_B8G8R8;
+      }
+      else if ((fixInfo->visual == FB_VISUAL_TRUECOLOR ||
+                fixInfo->visual == FB_VISUAL_DIRECTCOLOR)
+               && varInfo->bits_per_pixel == 32
+               && varInfo->red.offset == 16
+               && varInfo->green.offset == 8
+               && varInfo->blue.offset == 0
+               && varInfo->transp.offset == 24) {
+         vis->pixelFormat = PF_B8G8R8A8;
+      }
+      else if ((fixInfo->visual == FB_VISUAL_TRUECOLOR ||
+                fixInfo->visual == FB_VISUAL_DIRECTCOLOR)
+               && varInfo->bits_per_pixel == 16
+               && varInfo->red.offset == 11
+               && varInfo->green.offset == 5
+               && varInfo->blue.offset == 0) {
+         vis->pixelFormat = PF_B5G6R5;
+      }
+      else if ((fixInfo->visual == FB_VISUAL_TRUECOLOR ||
+                fixInfo->visual == FB_VISUAL_DIRECTCOLOR)
+               && varInfo->bits_per_pixel == 16
+               && varInfo->red.offset == 10
+               && varInfo->green.offset == 5
+               && varInfo->blue.offset == 0) {
+         vis->pixelFormat = PF_B5G5R5;
+      }
+      else {
+         _mesa_problem(NULL, "Unsupported fbdev RGB visual/bitdepth!\n");
+         /*
+         printf("fixInfo->visual = 0x%x\n", fixInfo->visual);
+         printf("varInfo->bits_per_pixel = %d\n", varInfo->bits_per_pixel);
+         printf("varInfo->red.offset = %d\n", varInfo->red.offset);
+         printf("varInfo->green.offset = %d\n", varInfo->green.offset);
+         printf("varInfo->blue.offset = %d\n", varInfo->blue.offset);
+         */
+         _mesa_free(vis);
+         return NULL;
+      }
+   }
+   else {
+      indexBits = varInfo->bits_per_pixel;
+      if ((fixInfo->visual == FB_VISUAL_PSEUDOCOLOR ||
+           fixInfo->visual == FB_VISUAL_STATIC_PSEUDOCOLOR)
+          && varInfo->bits_per_pixel == 8) {
+         vis->pixelFormat = PF_CI8;
+      }
+      else {
+         _mesa_problem(NULL, "Unsupported fbdev CI visual/bitdepth!\n");
+         _mesa_free(vis);
+         return NULL;
+      }
+   }
+
+   if (!_mesa_initialize_visual(&vis->glvisual, rgbFlag, dbFlag, stereoFlag,
+                                redBits, greenBits, blueBits, alphaBits,
+                                indexBits, depthBits, stencilBits,
+                                accumRedBits, accumGreenBits,
+                                accumBlueBits, accumAlphaBits,
+                                numSamples)) {
+      /* something was invalid */
+      _mesa_free(vis);
+      return NULL;
+   }
+
+   return vis;
+}
+
+
+void
+glFBDevDestroyVisual( GLFBDevVisualPtr visual )
+{
+   if (visual)
+      _mesa_free(visual);
+}
+
+
+int
+glFBDevGetVisualAttrib( const GLFBDevVisualPtr visual, int attrib)
+{
+   (void) visual;
+   (void) attrib;
+   return -1;
+}
+
+
+
+GLFBDevBufferPtr
+glFBDevCreateBuffer( const struct fb_fix_screeninfo *fixInfo,
+                     const struct fb_var_screeninfo *varInfo,
+                     const GLFBDevVisualPtr visual,
+                     void *frontBuffer, void *backBuffer, size_t size )
+{
+   GLFBDevBufferPtr buf;
+
+   ASSERT(visual);
+   ASSERT(frontBuffer);
+   ASSERT(size > 0);
+
+   if (visual->fix.visual != fixInfo->visual ||
+       visual->fix.type != fixInfo->type ||
+       visual->var.bits_per_pixel != varInfo->bits_per_pixel ||
+       visual->var.grayscale != varInfo->grayscale ||
+       visual->var.red.offset != varInfo->red.offset ||
+       visual->var.green.offset != varInfo->green.offset ||
+       visual->var.blue.offset != varInfo->blue.offset ||
+       visual->var.transp.offset != varInfo->transp.offset) {
+      /* visual mismatch! */
+      return NULL;
+   }
+
+   buf = CALLOC_STRUCT(GLFBDevBufferRec);
+   if (!buf)
+      return NULL;
+
+   _mesa_initialize_framebuffer(&buf->glframebuffer, &visual->glvisual,
+                                visual->glvisual.haveDepthBuffer,
+                                visual->glvisual.haveStencilBuffer,
+                                visual->glvisual.haveAccumBuffer,
+                                GL_FALSE);
+
+   buf->fix = *fixInfo;   /* struct assignment */
+   buf->var = *varInfo;   /* struct assignment */
+   buf->visual = visual;  /* ptr assignment */
+   buf->frontStart = frontBuffer;
+   buf->size = size;
+   buf->bytesPerPixel = visual->var.bits_per_pixel / 8;
+   buf->rowStride = visual->var.xres_virtual * buf->bytesPerPixel;
+   buf->frontBottom = (GLubyte *) buf->frontStart
+                    + (visual->var.yres_virtual - 1) * buf->rowStride;
+
+   if (visual->glvisual.doubleBufferMode) {
+      if (backBuffer) {
+         buf->backStart = backBuffer;
+         buf->mallocBackBuffer = GL_FALSE;
+      }
+      else {
+         buf->backStart = _mesa_malloc(size);
+         if (!buf->backStart) {
+            _mesa_free_framebuffer_data(&buf->glframebuffer);
+            _mesa_free(buf);
+            return NULL;
+         }
+         buf->mallocBackBuffer = GL_TRUE;
+      }
+      buf->backBottom = (GLubyte *) buf->backStart
+                      + (visual->var.yres_virtual - 1) * buf->rowStride;
+      buf->curBottom = buf->backBottom;
+   }
+   else {
+      buf->backStart = NULL;
+      buf->mallocBackBuffer = GL_FALSE;
+      buf->backBottom = NULL;
+      buf->curBottom = buf->frontBottom;
+   }
+
+   return buf;
+}
+
+
+void
+glFBDevDestroyBuffer( GLFBDevBufferPtr buffer )
+{
+   if (buffer) {
+      /* check if destroying the current buffer */
+      GLFBDevBufferPtr curDraw = glFBDevGetCurrentDrawBuffer();
+      GLFBDevBufferPtr curRead = glFBDevGetCurrentReadBuffer();
+      if (buffer == curDraw || buffer == curRead) {
+         glFBDevMakeCurrent( NULL, NULL, NULL);
+      }
+      if (buffer->mallocBackBuffer) {
+         _mesa_free(buffer->backStart);
+      }
+      /* free the software depth, stencil, accum buffers */
+      _mesa_free_framebuffer_data(&buffer->glframebuffer);
+      _mesa_free(buffer);
+   }
+}
+
+
+int
+glFBDevGetBufferAttrib( const GLFBDevBufferPtr buffer, int attrib)
+{
+   (void) buffer;
+   (void) attrib;
+   return -1;
+}
+
+
+GLFBDevBufferPtr
+glFBDevGetCurrentDrawBuffer( void )
+{
+   GLFBDevContextPtr fbdevctx = glFBDevGetCurrentContext();
+   if (fbdevctx)
+      return fbdevctx->drawBuffer;
+   else
+      return NULL;
+}
+
+
+GLFBDevBufferPtr
+glFBDevGetCurrentReadBuffer( void )
+{
+   GLFBDevContextPtr fbdevctx = glFBDevGetCurrentContext();
+   if (fbdevctx)
+      return fbdevctx->readBuffer;
+   else
+      return NULL;
+}
+
+
+void
+glFBDevSwapBuffers( GLFBDevBufferPtr buffer )
+{
+   GLFBDevContextPtr fbdevctx = glFBDevGetCurrentContext();
+
+   if (!buffer || !buffer->visual->glvisual.doubleBufferMode)
+      return;
+
+   /* check if swapping currently bound buffer */
+   if (fbdevctx->drawBuffer == buffer) {
+      /* flush pending rendering */
+      _mesa_notifySwapBuffers(&fbdevctx->glcontext);
+   }
+
+   ASSERT(buffer->frontStart);
+   ASSERT(buffer->backStart);
+   _mesa_memcpy(buffer->frontStart, buffer->backStart, buffer->size);
+}
+
+
+GLFBDevContextPtr
+glFBDevCreateContext( const GLFBDevVisualPtr visual, GLFBDevContextPtr share )
+{
+   GLFBDevContextPtr ctx;
+   GLcontext *glctx;
+
+   ASSERT(visual);
+
+   ctx = CALLOC_STRUCT(GLFBDevContextRec);
+   if (!ctx)
+      return NULL;
+
+   if (!_mesa_initialize_context(&ctx->glcontext, &visual->glvisual,
+                                 share ? &share->glcontext : NULL,
+                                 (void *) ctx, GL_FALSE)) {
+      _mesa_free(ctx);
+      return NULL;
+   }
+
+   ctx->visual = visual;
+
+   /* Create module contexts */
+   glctx = (GLcontext *) &ctx->glcontext;
+   init_core_functions( glctx );
+   _swrast_CreateContext( glctx );
+   _ac_CreateContext( glctx );
+   _tnl_CreateContext( glctx );
+   _swsetup_CreateContext( glctx );
+   _swsetup_Wakeup( glctx );
+
+   /* swrast init */
+   {
+      struct swrast_device_driver *swdd;
+      swdd = _swrast_GetDeviceDriverReference( glctx );
+      swdd->SetBuffer = set_buffer;
+      if (visual->pixelFormat == PF_B8G8R8) {
+         swdd->WriteRGBASpan = write_rgba_span_B8G8R8;
+         swdd->WriteRGBSpan = write_rgb_span_B8G8R8;
+         swdd->WriteMonoRGBASpan = write_monorgba_span_B8G8R8;
+         swdd->WriteRGBAPixels = write_rgba_pixels_B8G8R8;
+         swdd->WriteMonoRGBAPixels = write_monorgba_pixels_B8G8R8;
+         swdd->ReadRGBASpan = read_rgba_span_B8G8R8;
+         swdd->ReadRGBAPixels = read_rgba_pixels_B8G8R8;
+      }
+      else if (visual->pixelFormat == PF_B8G8R8A8) {
+         swdd->WriteRGBASpan = write_rgba_span_B8G8R8A8;
+         swdd->WriteRGBSpan = write_rgb_span_B8G8R8A8;
+         swdd->WriteMonoRGBASpan = write_monorgba_span_B8G8R8A8;
+         swdd->WriteRGBAPixels = write_rgba_pixels_B8G8R8A8;
+         swdd->WriteMonoRGBAPixels = write_monorgba_pixels_B8G8R8A8;
+         swdd->ReadRGBASpan = read_rgba_span_B8G8R8A8;
+         swdd->ReadRGBAPixels = read_rgba_pixels_B8G8R8A8;
+      }
+      else if (visual->pixelFormat == PF_B5G6R5) {
+         swdd->WriteRGBASpan = write_rgba_span_B5G6R5;
+         swdd->WriteRGBSpan = write_rgb_span_B5G6R5;
+         swdd->WriteMonoRGBASpan = write_monorgba_span_B5G6R5;
+         swdd->WriteRGBAPixels = write_rgba_pixels_B5G6R5;
+         swdd->WriteMonoRGBAPixels = write_monorgba_pixels_B5G6R5;
+         swdd->ReadRGBASpan = read_rgba_span_B5G6R5;
+         swdd->ReadRGBAPixels = read_rgba_pixels_B5G6R5;
+      }
+      else if (visual->pixelFormat == PF_B5G5R5) {
+         swdd->WriteRGBASpan = write_rgba_span_B5G5R5;
+         swdd->WriteRGBSpan = write_rgb_span_B5G5R5;
+         swdd->WriteMonoRGBASpan = write_monorgba_span_B5G5R5;
+         swdd->WriteRGBAPixels = write_rgba_pixels_B5G5R5;
+         swdd->WriteMonoRGBAPixels = write_monorgba_pixels_B5G5R5;
+         swdd->ReadRGBASpan = read_rgba_span_B5G5R5;
+         swdd->ReadRGBAPixels = read_rgba_pixels_B5G5R5;
+      }
+      else if (visual->pixelFormat == PF_CI8) {
+         swdd->WriteCI32Span = write_index32_span_CI8;
+         swdd->WriteCI8Span = write_index8_span_CI8;
+         swdd->WriteMonoCISpan = write_monoindex_span_CI8;
+         swdd->WriteCI32Pixels = write_index_pixels_CI8;
+         swdd->WriteMonoCIPixels = write_monoindex_pixels_CI8;
+         swdd->ReadCI32Span = read_index_span_CI8;
+         swdd->ReadCI32Pixels = read_index_pixels_CI8;
+      }
+      else {
+         _mesa_printf("bad pixelformat: %d\n", visual->pixelFormat);
+      }
+   }
+
+   /* use default TCL pipeline */
+   {
+      TNLcontext *tnl = TNL_CONTEXT(glctx);
+      tnl->Driver.RunPipeline = _tnl_run_pipeline;
+   }
+
+   _mesa_enable_sw_extensions(glctx);
+
+   return ctx;
+}
+
+
+void
+glFBDevDestroyContext( GLFBDevContextPtr context )
+{
+   GLFBDevContextPtr fbdevctx = glFBDevGetCurrentContext();
+
+   if (context) {
+      if (fbdevctx == context) {
+         /* destroying current context */
+         _mesa_make_current2(NULL, NULL, NULL);
+         _mesa_notifyDestroy(&context->glcontext);
+      }
+      _mesa_free_context_data(&context->glcontext);
+      _mesa_free(context);
+   }
+}
+
+
+int
+glFBDevGetContextAttrib( const GLFBDevContextPtr context, int attrib)
+{
+   (void) context;
+   (void) attrib;
+   return -1;
+}
+
+
+GLFBDevContextPtr
+glFBDevGetCurrentContext( void )
+{
+   GET_CURRENT_CONTEXT(ctx);
+   return (GLFBDevContextPtr) ctx;
+}
+
+
+int
+glFBDevMakeCurrent( GLFBDevContextPtr context,
+                    GLFBDevBufferPtr drawBuffer,
+                    GLFBDevBufferPtr readBuffer )
+{
+   if (context && drawBuffer && readBuffer) {
+      /* Make sure the context's visual and the buffers' visuals match.
+       * XXX we might do this by comparing specific fields like bits_per_pixel,
+       * visual, etc. in the future.
+       */
+      if (context->visual != drawBuffer->visual ||
+          context->visual != readBuffer->visual) {
+         return 0;
+      }
+      _mesa_make_current2( &context->glcontext,
+                           &drawBuffer->glframebuffer,
+                           &readBuffer->glframebuffer );
+      context->drawBuffer = drawBuffer;
+      context->readBuffer = readBuffer;
+      context->curBuffer = drawBuffer;
+   }
+   else {
+      /* unbind */
+      _mesa_make_current2( NULL, NULL, NULL );
+   }
+
+   return 1;
+}
+
+#endif /* USE_GLFBDEV_DRIVER */
+