/* vi: set sw=4 ts=4: */
/*
 * CCE - Console Chinese Environment -
 * Copyright (C) 1998-2003 Rui He (rhe@3eti.com)
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE TERRENCE R. LAMBERT BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * 
 */

#include	<stdio.h>
#include	<stdlib.h>
#include	<unistd.h>
#include	<fcntl.h>
#include	<string.h>

#include	"defs.h"
#include	"errors.h"
#include	"vc.h"
#include	"vga.h"
#include	"fb.h"

#ifdef HAVE_SYS_IOCTL_H
#include	<sys/ioctl.h>
#endif

#ifdef HAVE_SYS_MMAN_H
#include	<sys/mman.h>
#endif

#if defined(SUPPORT_FRAMEBUF)

#if defined(__FreeBSD__) 
int	FreeBSDVideoMode, origFreeBSDVideoMode;

#elif defined(__NetBSD__) || defined(__OpenBSD__)

#define SAVE_RESTORE_PALETTE 1

#elif defined(__Darwin__)

#include <mach/mach_interface.h>
#define NO_CFGPLUGIN
#include <IOKit/IOKitLib.h>
#include <IOKit/graphics/IOGraphicsLib.h>

static io_connect_t     fbService;

u_char DarwinColorMap8[8] =
{
	
	0xFF,    0xD2, 0xB9,  0xB4, 0x23, 0x18,    0x05,   0x00
};

#undef SAVE_RESTORE_PALETTE   /* disable palette save/restore on Darwin, doesn't work! */

#endif  /* Darwin */

#undef DEBUG /* Rui He, not debugging VgaFB */

FBCMAP fb_cmap;

static u_int MmapOffset;

#if defined(SAVE_RESTORE_PALETTE)

#define HANDLE_PALETTE	(dispInfo.bpp <= 8) /* palette-based ? */

#if defined(linux) || defined(__Darwin__)

typedef u_short CMAPTYPE;

#elif defined(__FreeBSD__)  || defined(__NetBSD__) || defined(__OpenBSD__)
typedef u_char CMAPTYPE;
#endif

static int savedPel; /* save Palette successfully ? */
static CMAPTYPE savedRed[MAX_PELS], savedGreen[MAX_PELS],savedBlue[MAX_PELS];

 static int VgaFBSetPels(CMAPTYPE *red, CMAPTYPE *green, CMAPTYPE *blue)
{
	int ret = -1; 

#if defined(linux)
   struct fb_cmap cmap;

   cmap.start = 0;
   cmap.len = MAX_PELS;
   cmap.red = (CMAPTYPE *)red;
   cmap.green = (CMAPTYPE *)green;
   cmap.blue = (CMAPTYPE *)blue;

   ret = ioctl(dispInfo.fd_fb, FBIOPUTCMAP, &cmap);

#elif defined(__FreeBSD__)
   struct fbcmap cmap;     /* int index,count; u_char *red, *green, *blue */

   cmap.index = 0;
   cmap.count = MAX_PELS;
   cmap.red = (CMAPTYPE *)red; 
   cmap.green = (CMAPTYPE *)green;
   cmap.blue = (CMAPTYPE *)blue;

   ret = ioctl(0, FBIOPUTCMAP, &cmap);

#elif defined(__NetBSD__) || defined(__OpenBSD__)
   struct wsdisplay_cmap cmap;     /* int index,count; u_char *red, *green, *blue */

   cmap.index = 0;
   cmap.count = MAX_PELS;
   cmap.red = (CMAPTYPE *)red; 
   cmap.green = (CMAPTYPE *)green;
   cmap.blue = (CMAPTYPE *)blue;

   ret = ioctl(0, WSDISPLAYIO_PUTCMAP, &cmap);

#elif defined(__Darwin__)
	IOColorEntry newColors[MAX_PELS];
	int i;
   
	if (!fbService) return; /* just for safety */
	for(i = 0; i < MAX_PELS; i++)
	{
		newColors[i].index = i;
		newColors[i].red = red[i];
		newColors[i].green = green[i];
		newColors[i].blue = blue[i];
	}

	if ( IOFBSetCLUT(fbService, 0/*StartIndex */, MAX_PELS, kSetCLUTByValue, newColors) 
		== KERN_SUCCESS) ret = 0;
#endif
	  
	if (ret) /* 0 is success */
		error("Failed to set palette!\r\n");  /* not fatal! */
	return ret;
}

static int VgaFBGetPels(CMAPTYPE *red, CMAPTYPE *green, CMAPTYPE *blue)
{
	int ret = -1;

#if defined(linux)
    struct fb_cmap cmap;

    cmap.red = (CMAPTYPE *)red;
    cmap.green = (CMAPTYPE *)green;
    cmap.blue = (CMAPTYPE *)blue;
    cmap.start = 0;
    cmap.len = MAX_PELS;

    ret = ioctl(dispInfo.fd_fb, FBIOGETCMAP, &cmap);

#elif defined(__FreeBSD__)
	struct fbcmap cmap;

	cmap.red = (CMAPTYPE *)red; 
	cmap.green = (CMAPTYPE *)green;
	cmap.blue = (CMAPTYPE *)blue;
	cmap.index = 0;
	cmap.count = MAX_PELS;
	
	ret = ioctl(0, FBIOGETCMAP, &cmap);

#elif defined(__NetBSD__) || defined(__OpenBSD__)
	struct wsdisplay_cmap cmap;

	cmap.red = (CMAPTYPE *)red; 
	cmap.green = (CMAPTYPE *)green;
	cmap.blue = (CMAPTYPE *)blue;
	cmap.index = 0;
	cmap.count = MAX_PELS;
	
	ret = ioctl(0, WSDISPLAYIO_GETCMAP, &cmap);

#elif defined(__Darwin__)

#endif

	return ret;
}

#else

#define HANDLE_PALETTE	(0)

#endif /* defined(SAVE_RESTORE_PALETTE) */

static u_int mask_bits[] = { 0x0, 0x1, 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f, 0xff};

#define COLORBITS1(color, offset, length) ( ((mask_bits[length] << (16 - (length))) & (color)) >> (16 - (length) - (offset)) )
#define COLORBITS2(color, offset, length)  (( mask_bits[length] & (color)) << (offset))

void VgaFBInitColorMap(void)
{
    int i;
    u_int red, green, blue;

    for(i = 0; i < 16; i++)
    {
        red = red16[i];
        green = green16[i];
        blue = blue16[i];

        switch(dispInfo.bpp)
        { 
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
		case 15:
            fb_cmap.cfb16[i] =  ((red   & 0xf800) >> 1) |
                                ((green & 0xf800) >> 6) |
                                ((blue  & 0xf800) >> 11); 
			break;	
        case 16:  /* 0:5:6:5, usually green has 6 bits */
            fb_cmap.cfb16[i] =  ((red   & 0xf800) ) |
                                ((green & 0xfc00) >> 5) |
                                ((blue  & 0xf800) >> 11);
            break;
		case 24:
            fb_cmap.cfb24[i] = ((red  & 0xff00) << 8) | 
                               (green & 0xff00) | 
                               (blue >> 8);
			break;
        case 32:
            fb_cmap.cfb32[i] = ((red  & 0xff00) << 8) |
                               (green & 0xff00) |
                               (blue >> 8);
			break;	
 
#else  /* if defined(linux) || defined(__Darwin__) */
        case 15:  /* 1:5:5:5 */
		case 16:
			fb_cmap.cfb16[i] = COLORBITS1(red, dispInfo.redoff, dispInfo.redlen) |
							   COLORBITS1(green, dispInfo.greenoff, dispInfo.greenlen) |
							   COLORBITS1(blue, dispInfo.blueoff, dispInfo.bluelen) ;
			
            break;
       case 24:
			fb_cmap.cfb24[i] = COLORBITS2(red, dispInfo.redoff, dispInfo.redlen) |
                               COLORBITS2(green, dispInfo.greenoff, dispInfo.greenlen) |
                               COLORBITS2(blue, dispInfo.blueoff, dispInfo.bluelen) ;
            break;
        case 32:
			fb_cmap.cfb32[i] = COLORBITS2(red, dispInfo.redoff, dispInfo.redlen) |
                               COLORBITS2(green, dispInfo.greenoff, dispInfo.greenlen) |
                               COLORBITS2(blue, dispInfo.blueoff, dispInfo.bluelen) ;
            break;            
#endif
        }
    }

}

#ifdef SUPPORT_FREETYPE_FONT

#include "ftfont.h"
 
static inline u_int FBGetPixelColor(int color)
{
	switch(dispInfo.bpp)
	{
	case 8:
		return color;  /* palette, background */
	case 15:
	case 16:
		return fb_cmap.cfb16[color];
	case 24:
		return fb_cmap.cfb24[color];
	case 32:
		return fb_cmap.cfb32[color];
	}
}
	
void FBDrawFreeTypeChar(int xpos, int ypos, FT_GlyphSlot glyph, int fc, int bc, int width, int height)
{	int i,j, k;
	u_char bits, *code;
	u_char *dest, *line;
	u_int pix;
    int top, left, row, col, pitch;
	u_short red,green, blue;

	if (glyph == NULL)
	{
		left=top=row=col=pitch=0;
		code = NULL;
	}
	else
	{
		left = ftXOrig + glyph->bitmap_left;  /* bitmap_left should be >= 0 ? */
		top = ftYOrig - glyph->bitmap_top;
		if (top < 0) top = 0;
		row = glyph->bitmap.rows;  /* rows/width >= 0 */
		col = glyph->bitmap.width;
		code = glyph->bitmap.buffer;
		pitch = glyph->bitmap.pitch; /* bitmap scan line bytes */
		
	}

	line = gramMem + ypos * dispInfo.glineByte + xpos * dispInfo.bpp/8;

	for(i = 0; i < height; i++)
	{
		dest = line;
		k = 0;
		for(j = 0; j < width; j++)
		{
			if (i < top || i >= top+row || j < left || j >= left+col)
			{
				pix = FBGetPixelColor(bc);
			}
			else  /* in freetype font region */
			{
				if (glyph->bitmap.pixel_mode == ft_pixel_mode_mono) /* bit oriented */
				{
					if ((k & 0x7) == 0) bits = code[k/8];
				    pix = FBGetPixelColor( bits & (0x80 >> (k & 0x7)) ? fc : bc );
				}
				else
				{
					bits = code[k]; /* 0-255 gray scale */

					if (!DoAntiAlias) bits = (bits >= 128? 255: 0);

                   	red = (unsigned short)(red16[bc] + ((red16[fc]-red16[bc]) * bits)/255);
                    green = (unsigned short)(green16[bc] + ((green16[fc]-green16[bc]) * bits)/255);
                    blue = (unsigned short)(blue16[bc] + ((blue16[fc]-blue16[bc]) * bits)/255);
					switch(dispInfo.bpp)
					{
					case 8:
						pix = (bits >= 128 ? fc : bc);  /* no anti-alias */
						break;
					case 15:
					case 16:
						pix = COLORBITS1(red, dispInfo.redoff, dispInfo.redlen) |
							  COLORBITS1(green, dispInfo.greenoff, dispInfo.greenlen) |
							  COLORBITS1(blue, dispInfo.blueoff, dispInfo.bluelen) ;
						break;
					case 24:
					case 32:
						pix = ((red  & 0xff00) << 8) | (green & 0xff00) | (blue >> 8);
						break;
					}

				}
				k++;
			}

			switch(dispInfo.bpp)
			{
			case 8:
#ifdef __Darwin__
				pix = DarwinColorMap8[pix & DARWIN_COLOR_MASK];
#endif
				*dest++ = (u_char)(pix);
				break;
			case 15:
			case 16:
				*((u_short *)dest)++ = (u_short)(pix); 
				break;
			case 24:
				*dest++ = (u_char)(pix); pix >>= 8;
				*dest++ = (u_char)(pix); pix >>= 8;
				*dest++ = (u_char)(pix);
				break;
			case 32:
				*((u_int *)dest)++ = (u_int)(pix);
				break;
			}
		}

		line += dispInfo.glineByte;
		if (k > 0) /* k>0 means code is used somehow,  code could be NULL */
            code += pitch;

	}
}

#endif /* SUPPORT_FREETYPE_FONT */

#if defined(linux)

static int InitLinuxFB(void)
{
    int fd, res;

    struct fb_fix_screeninfo fix;
    struct fb_var_screeninfo var;

    if ((fd = open(DEFAULT_FB, O_RDWR)) < 0)
		fd = open(DEFAULT_FB_DEVFS, O_RDWR);

    if (fd < 0 || ioctl(fd, FBIOGET_FSCREENINFO, &fix)  ||
        ioctl(fd, FBIOGET_VSCREENINFO, &var))
		return FAILURE;

#ifdef DEBUG
	fprintf(stderr, "x_vir=%d y_vir=%d xoff=%d yoff=%d\n", var.xres_virtual, var.yres_virtual, var.xoffset, var.yoffset);
#endif

	var.xres_virtual = var.xres;
	var.yres_virtual = var.yres;
	var.xoffset = var.yoffset = 0;
	ioctl(fd, FBIOPUT_VSCREENINFO, &var); /* failure not critical? */
	
	dispInfo.gxdim = var.xres;
    dispInfo.gydim = var.yres;
	dispInfo.bpp = var.bits_per_pixel;
	dispInfo.glineByte = fix.line_length;

	dispInfo.redlen = var.red.length;
	dispInfo.redoff = var.red.offset;
	dispInfo.greenlen = var.green.length;
	dispInfo.greenoff = var.green.offset;
	dispInfo.bluelen = var.blue.length;
	dispInfo.blueoff = var.blue.offset;
	
	dispInfo.fd_fb = fd;

	MmapOffset = 0; /* linux, always start at 0 */
	
#if defined(FB_TYPE_VGA_PLANES)   /* Linux 2.0.x/2.2.x  has no VGA_PLANES fb? */
	if (fix.type == FB_TYPE_VGA_PLANES)  /* EGA/VGA planes */
	{
		dispInfo.type = TYPE_FB_VGAPLANES;
		if (dispInfo.bpp != 4)
		{
			message("Unsupported VGAPLANES frame buffer type(depth=%d)!\n", dispInfo.bpp);
			return FAILURE;
		}

		dispInfo.bpp = 1;
		
#if defined(CCE_TARGETARCH_I386)
		ioperm(VGAGRP_ADDR, 1, 1);
   		ioperm(VGAGRP_DATA, 1, 1);
#endif
	}
	else
#endif /* FB_TYPE_VGA_PLANES */
	{
		if (fix.type != FB_TYPE_PACKED_PIXELS)  
		{
			message("Unsupported frame buffer type!\n");
			return FAILURE;
		}
		dispInfo.type = TYPE_FB_LINEAR;
	}

	return SUCCESS;
}

#elif defined(__FreeBSD__) 
static int InitFreeBSDFB(void)
{
	int mode;
    video_info_t vmode; 

	origFreeBSDVideoMode = GetOrigFreeBSDVideoMode();
	
	FreeBSDVideoMode = -1;
	if (fbwidth > 0 && fbheight > 0 && fbdepth > 0)
		FreeBSDVideoMode = GetFreeBSDVideoMode( fbwidth, fbheight, fbdepth, &vmode); 
	if (FreeBSDVideoMode == -1)  /* one more try */
		FreeBSDVideoMode = GetBestFreeBSDConsVidMode(&vmode);

	if (FreeBSDVideoMode == -1)
	{
		message("Unsupported framebuffer, please try 'kldload vesa'.\n");
		return FAILURE;
	}

	dispInfo.gxdim = vmode.vi_width;
	dispInfo.gydim = vmode.vi_height;

	if (vmode.vi_mem_model == V_INFO_MM_PLANAR) /* 1 */
	{
		if (vmode.vi_depth != 4 || vmode.vi_planes != 4)
		{
			message("Unsupported Planar Framebuffer(depth=%d, planes=%d).\n",
					vmode.vi_depth, vmode.vi_planes);
			return FAILURE;
		}
		
		dispInfo.bpp = 1;
		dispInfo.type = TYPE_FB_VGAPLANES;
		dispInfo.glineByte = dispInfo.gxdim/8;

#if defined(CCE_TARGETARCH_I386)
   		i386_set_ioperm(VGAGRP_ADDR, 1, 1);
   		i386_set_ioperm(VGAGRP_DATA, 1, 1);
#endif		
	}		
	else
	{
		if (vmode.vi_mem_model != V_INFO_MM_PACKED && 
			vmode.vi_mem_model != V_INFO_MM_DIRECT)
		{
			message("Unsupported Framebuffer Type!\n");
			return FAILURE;
		}

		dispInfo.bpp = vmode.vi_depth;
		dispInfo.type = TYPE_FB_LINEAR;

		dispInfo.glineByte = vmode.vi_pixel_size * vmode.vi_width;
		dispInfo.redlen = vmode.vi_pixel_fsizes[0];
		dispInfo.redoff = vmode.vi_pixel_fields[0];
		dispInfo.greenlen = vmode.vi_pixel_fsizes[1];
		dispInfo.greenoff = vmode.vi_pixel_fields[1];
		dispInfo.bluelen = vmode.vi_pixel_fsizes[2];
		dispInfo.blueoff = vmode.vi_pixel_fields[2];
	}

	dispInfo.fd_fb = 0; 
	MmapOffset = 0; 

	return SUCCESS;
}
	
#elif defined(__Darwin__)

#define kern_assert(x) { if ((x) != KERN_SUCCESS) return FAILURE; }

static int InitDarwinFB(void)
{
	io_iterator_t	fbIter;
	io_service_t	service;
	mach_port_t     masterPort;
	kern_return_t 	kr;

	IODisplayModeInformation    modeInfo;
	IODisplayModeID		        displayMode;
	IOIndex		                displayDepth;
	IOFramebufferInformation	fbInfo;
	IOPixelInformation		    pixelInfo;

	kr = IOMasterPort(bootstrap_port, &masterPort);
	kern_assert(kr);

	kr = IOServiceGetMatchingServices(masterPort, 
			IOServiceMatching(IOFRAMEBUFFER_CONFORMSTO), &fbIter);
	kern_assert(kr);

	service = IOIteratorNext(fbIter);  /* first screen, normally 32bpp? */
	if (service == 0) return FAILURE;

	kr = IOServiceOpen(service, mach_task_self(), kIOFBServerConnectType, &fbService);
	kern_assert(kr);
	IOObjectRelease(service);

	kr = IOFBCreateSharedCursor( fbService, kIOFBCurrentShmemVersion, 32, 32 );
	kern_assert(kr);

	kr = IOFBGetCurrentDisplayModeAndDepth(fbService, &displayMode, &displayDepth);
	kern_assert(kr);

	kr = IOFBGetDisplayModeInformation(fbService, displayMode, &modeInfo);
	kern_assert(kr);
#ifdef DEBUG
	fprintf(stderr, "modeinfo.maxDepthIndex=%d\n", modeInfo.maxDepthIndex);
#endif

    kr = IOFBGetPixelInformation( fbService, displayMode, displayDepth,
                                  kIOFBSystemAperture, &pixelInfo );
	kern_assert(kr);

    kr = IOFBGetFramebufferInformationForAperture( fbService, kIOFBSystemAperture, &fbInfo );
	kern_assert(kr);

    if (fbInfo.activeWidth <= 1 || fbInfo.activeHeight <= 1) {
        message("Discarding screen with width %d and height %d\r\n", 
				fbInfo.activeWidth, fbInfo.activeHeight);
        return FAILURE;
    }

#ifdef DEBUG

	fprintf(stderr, "w=%d h=%d pitch=%d bpp=%d PixCompCnt=%d BitsPerComp=%d\n", 
			fbInfo.activeWidth, fbInfo.activeHeight, fbInfo.bytesPerRow, 
			fbInfo.bitsPerPixel, pixelInfo.componentCount, pixelInfo.bitsPerComponent);
#endif

	dispInfo.gxdim = fbInfo.activeWidth;
    dispInfo.gydim = fbInfo.activeHeight;
	dispInfo.bpp = fbInfo.bitsPerPixel;
	if (dispInfo.bpp == 16) dispInfo.bpp = 15;  /* Apple only use 1555 format ?*/
	dispInfo.glineByte = fbInfo.bytesPerRow;
	dispInfo.type = TYPE_FB_LINEAR;
	dispInfo.fd_fb = 0;  /* a fake one, not used, <0 for error! */
	MmapOffset = 0;   /* not used */
	
	if (dispInfo.bpp == 15)
	{
		dispInfo.redlen = dispInfo.greenlen = dispInfo.bluelen = 5;
		dispInfo.redoff = 10;
		dispInfo.greenoff = 5;
		dispInfo.blueoff = 0;
	}
	else  /* 8, 24, 32bpp, Apple's 24bpp is same as 32 bpp? */
	{
		dispInfo.redlen = dispInfo.greenlen = dispInfo.bluelen = 8;
		dispInfo.redoff = 16;
		dispInfo.greenoff = 8;
		dispInfo.blueoff = 0;
	}

	return SUCCESS;

}

#elif defined(__OpenBSD__) || defined(__NetBSD__)

#include <time.h>  /* for NetBSD */
#include <dev/wscons/wsconsio.h>  /* for all the WSDISPLAYIO_* ioctls */

static int InitWsconsFB(void)
{
    int display_type, display_pitch;
	struct wsdisplay_fbinfo display_fb;
	int mode = WSDISPLAYIO_MODE_MAPPED;  /* 1 */

	if (ioctl(0, WSDISPLAYIO_GTYPE, &display_type) == -1 ||
        ioctl(0, WSDISPLAYIO_GINFO, &display_fb) == -1 
#if defined(__OpenBSD__)  /* NetBSD has no such ioctl */
        || ioctl(0, WSDISPLAYIO_LINEBYTES, &display_pitch) == -1
#endif
	    )
	{
		message("Framebuffer is not supported on this platform.\r\n");
		return FAILURE;
	}
	
	if (display_fb.depth < 8) 
	{
		message("Unsupported framebuffer depth: %d\r\n", display_fb.depth);
		return FAILURE;
	}

	dispInfo.gxdim = display_fb.width;
    dispInfo.gydim = display_fb.height;
	dispInfo.bpp = display_fb.depth;
#if defined(__OpenBSD__)
	dispInfo.glineByte = display_pitch;
#else
	dispInfo.glineByte = ((dispInfo.bpp + 7)/8) * dispInfo.gxdim;

#endif

	dispInfo.type = TYPE_FB_LINEAR;
	dispInfo.fd_fb = 0;  /* 0 is stdin, should be same as /dev/ttyCn */
	MmapOffset = 0;   /* OpenBSD mmap at offset 0 on /dev/ttyCn */
	
	ioctl(0, WSDISPLAYIO_SMODE, &mode);

	return SUCCESS;

}

#endif /* linux, *BSD, Darwin */

static int VgaFBInitRegs(void)
{
	int ret = FAILURE;
	
#if defined(linux)
	ret = InitLinuxFB();
#elif defined(__FreeBSD__)
	ret = InitFreeBSDFB();
#elif defined(__NetBSD__) || defined(__OpenBSD__)
	ret = InitWsconsFB();
#elif defined(__Darwin__)
	ret = InitDarwinFB();
#else
#error Framebuffer only supported on Linux/FreeBSD/NetBSD/OpenBSD/Darwin !
#endif

	if (ret != SUCCESS || dispInfo.fd_fb < 0 || dispInfo.gxdim < 320 || 
			dispInfo.gydim < 240)  
		return FAILURE;

	InitDispInfo();

	if (dispInfo.type == TYPE_FB_VGAPLANES)  /* EGA/VGA planes */
	{
		CheckSuidRoot();  /* make sure it's suid-root */

		if (fontwidth !=8  || fontheight != 16)
			fatal("Only 16x16 font is supported in 640x480x4/800x600x4 mode.\r\n");
		message("Using %dx%d 4-bpp(VGAPlane) framebuffer, %dx%d terminal.\n", 
            dispInfo.gxdim, dispInfo.gydim, dispInfo.txmax, dispInfo.tymax);

	}
	else /* TYPE_FB_LINEAR */
	{
		if ( (dispInfo.bpp != 4 && dispInfo.bpp != 8 && dispInfo.bpp != 16 && 
		     dispInfo.bpp != 15 && dispInfo.bpp != 24 && dispInfo.bpp != 32) || 
		     (dispInfo.bpp >= 15 && 
		       (dispInfo.redlen > 8 || dispInfo.redlen <= 0 || 
		        dispInfo.greenlen > 8 || dispInfo.greenlen <=0 || 
		        dispInfo.bluelen > 8 || dispInfo.bluelen <= 0 )) )
		{
			message("Unsupported color depth %d bpp(RGB %d:%d:%d)\r\n", 
			dispInfo.bpp, dispInfo.redlen, dispInfo.greenlen, dispInfo.bluelen);
			return FAILURE;
		}

		message("Using %dx%d %d-bpp(RGB %d:%d:%d) framebuffer, %dx%d terminal.\n", 
        	dispInfo.gxdim, dispInfo.gydim, dispInfo.bpp, dispInfo.redlen, 
	 		dispInfo.greenlen, dispInfo.bluelen, dispInfo.txmax, dispInfo.tymax);
	
		if (dispInfo.bpp > 8)
    		VgaFBInitColorMap(); /* mainly for 15~32 bpps */

    	if (dispInfo.bpp == 15) dispInfo.bpp = 16; 

	}

	return SUCCESS;
}

int VgaFBAttach(void)
{
#if defined(linux) || defined(__FreeBSD__)
	if (dispInfo.type == TYPE_FB_VGAPLANES)
	{
    	if (ioctl(0, KDENABIO,0) < 0) 
    	{
			warn("ioctl CONSOLE_IO_ENABLE"); /* not critical? */
		}
    }
#endif

#if defined(__FreeBSD__)
	if (ioctl(0, FreeBSDVideoMode, 0) == -1) 
	{
		Perror("ioctl switch video mode fail");
		return FAILURE;
	}
	if (dispInfo.type == TYPE_FB_VGAPLANES) VgaResetMode();
#endif

#if defined(__Darwin__)
	{
		vm_address_t    vram;
 		vm_size_t       shmemSize;

    	if (IOConnectMapMemory( fbService, kIOFBSystemAperture, mach_task_self(), 
				&vram, &shmemSize, kIOMapAnywhere ) != KERN_SUCCESS)
			return FAILURE;

		gramMem = (u_char *)vram;  /* no need to do mmap for Darwin */
	}	
#else

    gramMem = (u_char *)mmap(
#if defined(linux)
		(__ptr_t)0, 
#else
		0,
#endif
	  dispInfo.gsize, PROT_READ|PROT_WRITE, 
	  MAP_SHARED, dispInfo.fd_fb, MmapOffset);

    if ((void *)gramMem == MAP_FAILED) 
    {
		Perror("mmap framebuffer memory fail");
		return FAILURE;
    }
#endif

#ifdef DEBUG
	fprintf(stderr, "Now doing  VgaFBAttach, gramMem=%p\n", gramMem);
#endif

    return SUCCESS;
}

void VgaFBDetach(void)
{
#if defined(__FreeBSD__)
	ioctl(0, SW_TEXT_80x25, 0);
#endif

	if (dispInfo.type == TYPE_FB_VGAPLANES)
	{
#if defined(CCE_TARGETARCH_I386)
#if defined(linux)
   		ioperm(VGAGRP_ADDR, 1, 0);
   		ioperm(VGAGRP_DATA, 1, 0);
#elif defined(__FreeBSD__)
   		i386_set_ioperm(VGAGRP_ADDR, 1, 0);
   		i386_set_ioperm(VGAGRP_DATA, 1, 0);
#endif
#endif
	}

#if defined(__Darwin__)
	if (fbService)
	{
		if (gramMem) IOConnectUnmapMemory(fbService, kIOFBSystemAperture, 
				mach_task_self(), (vm_address_t)gramMem);
		IOServiceClose(fbService);   /* necessary ? */
		fbService = 0;
	}
#else
	if (gramMem && (void *)gramMem != MAP_FAILED)  munmap(gramMem, dispInfo.gsize);
#endif
	gramMem = 0;  /* set to zero to capture error! */
	
	if (dispInfo.fd_fb > 0) close(dispInfo.fd_fb); 
	dispInfo.fd_fb = -1;

}

int VgaFBInit(void)
{
    if (VgaFBInitRegs() != SUCCESS) return FAILURE;

    return VgaFBAttach();
}

void VgaFBScreenSaver(bool blank)
{
}
 
void VgaFBTextMode(void)
{

#if defined(__FreeBSD__)
	ioctl(0, origFreeBSDVideoMode, 0);
#endif

#if defined(SAVE_RESTORE_PALETTE) && !defined(__Darwin__)
	if (HANDLE_PALETTE)
	{
    	if (savedPel) VgaFBSetPels(savedRed, savedGreen, savedBlue);
	}
#endif

}

void VgaFBGraphMode(void)
{
#if defined(__FreeBSD__) 

	ioctl(0, FreeBSDVideoMode, 0);

	if (dispInfo.type == TYPE_FB_VGAPLANES) VgaResetMode();
#endif

#if defined(SAVE_RESTORE_PALETTE)
	if (HANDLE_PALETTE)
	{
#if defined(linux) || defined(__Darwin__)
		VgaFBSetPels(red16, green16, blue16);
#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
	 	VgaFBSetPels(red8, green8, blue8);
#endif
	}
#endif

	CCEClear(CLEAR_DISPLAY, DEF_BGCOLOR);

}

void VgaFBStart(void)
{
	cursorTop = fontheight - 2;
	cursorBtm = fontheight - 1;

#if defined(SAVE_RESTORE_PALETTE) && !defined(__Darwin__)

	if (HANDLE_PALETTE)
	{
		if (VgaFBGetPels(savedRed, savedGreen, savedBlue) == 0)
    		savedPel = 1;
	}
#endif

}

VideoInfo VgaFBInfo =
{
    VgaFBInit,
    VgaFBStart,
    VgaFBTextMode,
    VgaFBGraphMode,
    CCEWput,
    CCEInputWput,
    CCESput,
    CCEInputSput,
	CCEInputDrawIcon,
    CCECursor,
    CCEClear,
    VgaFBScreenSaver,
    VgaFBDetach
};

#endif
