efl/src/modules/evas/engines/fb/evas_fb_main.c

623 lines
17 KiB
C

/* -------------------------------------------------------------------- */
/* LINUX FBCON FRAMEBUFFER UTILITY CODE */
/* makes setting up the framebuffer easy. Also makes it eays to port to */
/* some other system if needed. */
/* Copyright (c) 1999 - Carsten Haitzler (The Rasterman) */
/* -------------------------------------------------------------------- */
#include "evas_common_private.h"
#include "evas_fb.h"
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <linux/kd.h>
#include <linux/vt.h>
#include <sys/user.h>
#define FB_ACTIVE 0
#define FB_REL_REQ 1
#define FB_INACTIVE 2
#define FB_ACQ_REQ 3
/* -------------------------------------------------------------------- */
/* internal variables */
static struct fb_fix_screeninfo fb_fix;
static int fb = -1, tty = -1;
static int bpp, depth;
//static int orig_vt_no = 0;
static int kd_mode;
static struct vt_mode vt_omode;
static struct fb_var_screeninfo fb_ovar;
static unsigned short ored[256], ogreen[256], oblue[256];
static unsigned short red[256], green[256], blue[256];
static struct fb_cmap ocmap = { 0, 256, ored, ogreen, oblue, NULL };
static struct fb_cmap cmap = { 0, 256, red, green, blue, NULL };
/* -------------------------------------------------------------------- */
/* internal function prototypes */
//static void fb_cleanup_fork(void);
//static void fb_setvt(int vtno);
static void fb_init_palette_332(FB_Mode *mode);
static void fb_init_palette_linear(FB_Mode *mode);
/* -------------------------------------------------------------------- */
/* palette setting */
static void
fb_init_palette_332(FB_Mode *mode)
{
int r, g, b, i;
if (mode->fb_var.bits_per_pixel != 8)
return;
i = 0;
if (ioctl(fb, FBIOGETCMAP, &cmap) == -1)
perror("ioctl FBIOGETCMAP");
/* generate the palette */
for (r = 0; r < 8; r++)
{
for (g = 0; g < 8; g++)
{
for (b = 0; b < 4; b++)
{
int val;
val = (r << 5) | (r << 2) | (r >> 1);
red[i] = (val << 8) | val;
val = (g << 5) | (g << 2) | (g >> 1);
green[i] = (val << 8) | val;
val = (b << 6) | (b << 4) | (b << 2) | (b);
blue[i] = (val << 8) | val;
i++;
}
}
}
/* set colormap */
if (ioctl(fb, FBIOPUTCMAP, &cmap) == -1)
perror("ioctl FBIOPUTCMAP");
}
static void
fb_init_palette_linear(FB_Mode *mode)
{
int i;
if (mode->fb_var.bits_per_pixel != 8)
return;
if (ioctl(fb, FBIOGETCMAP, &cmap) == -1)
perror("ioctl FBIOGETCMAP");
/* generate the palette */
for (i = 0; i < 256; i++)
red[i] = (i << 8) | i;
for (i = 0; i < 256; i++)
green[i] = (i << 8) | i;
for (i = 0; i < 256; i++)
blue[i] = (i << 8) | i;
/* set colormap */
if (ioctl(fb, FBIOPUTCMAP, &cmap) == -1)
perror("ioctl FBIOPUTCMAP");
}
/* -------------------------------------------------------------------- */
/* initialisation & cleanup */
FB_Mode *
fb_list_modes(unsigned int *num_return)
{
FILE *f;
char line[256], label[256], value[256];
FB_Mode *modes = NULL;
int num;
num = 0;
f = fopen("/etc/fb.modes","r");
if (!f)
{
*num_return = 0;
return NULL;
}
while (fgets(line, sizeof(line) - 1, f))
{
if (sscanf(line, "mode \"%250[^\"]\"", label) == 1)
{
char f1[32], f2[32], f3[32], f4[32];
f1[0] = 0; f2[0] = 0; f3[0] = 0; f4[0] = 0;
sscanf(label, "%30[^x]x%30[^-]-%30[^-]-%30s", f1, f2, f3, f4);
if ((f1[0]) && (f2[0]))
{
int geometry = 0;
int timings = 0;
num++;
modes = realloc(modes, num * sizeof(FB_Mode));
modes[num - 1].width = atoi(f1);
modes[num - 1].height = atoi(f2);
if (f3[0])
modes[num - 1].refresh = atoi(f3);
else
modes[num - 1].refresh = 0;
modes[num - 1].fb_var.sync = 0;
while ((fgets(line, sizeof(line) - 1, f)) &&
(!strstr(line, "endmode")))
{
if (sscanf(line," geometry %i %i %i %i %i",
&modes[num - 1].fb_var.xres,
&modes[num - 1].fb_var.yres,
&modes[num - 1].fb_var.xres_virtual,
&modes[num - 1].fb_var.yres_virtual,
&modes[num - 1].fb_var.bits_per_pixel) == 5)
geometry = 1;
if (sscanf(line," timings %i %i %i %i %i %i %i",
&modes[num - 1].fb_var.pixclock,
&modes[num - 1].fb_var.left_margin,
&modes[num - 1].fb_var.right_margin,
&modes[num - 1].fb_var.upper_margin,
&modes[num - 1].fb_var.lower_margin,
&modes[num - 1].fb_var.hsync_len,
&modes[num - 1].fb_var.vsync_len) == 7)
timings = 1;
if ((sscanf(line, " hsync %15s", value) == 1) &&
(!strcmp(value,"high")))
modes[num - 1].fb_var.sync |= FB_SYNC_HOR_HIGH_ACT;
if ((sscanf(line, " vsync %15s", value) == 1) &&
(!strcmp(value,"high")))
modes[num - 1].fb_var.sync |= FB_SYNC_VERT_HIGH_ACT;
if ((sscanf(line, " csync %15s", value) == 1) &&
(!strcmp(value,"high")))
modes[num - 1].fb_var.sync |= FB_SYNC_COMP_HIGH_ACT;
if ((sscanf(line, " extsync %15s", value) == 1) &&
(!strcmp(value,"true")))
modes[num - 1].fb_var.sync |= FB_SYNC_EXT;
if ((sscanf(line, " laced %15s", value) == 1) &&
(!strcmp(value,"true")))
modes[num - 1].fb_var.vmode |= FB_VMODE_INTERLACED;
if ((sscanf(line, " double %15s",value) == 1) &&
(!strcmp(value,"true")))
modes[num - 1].fb_var.vmode |= FB_VMODE_DOUBLE;
}
if ((!geometry) || (!timings))
{
num--;
if (num == 0)
{
free(modes);
modes = NULL;
}
}
else
{
modes[num - 1].fb_var.xoffset = 0;
modes[num - 1].fb_var.yoffset = 0;
}
}
}
}
fclose(f);
*num_return = num;
return modes;
}
FB_Mode *
fb_setmode(unsigned int width, unsigned int height, unsigned int pdepth, unsigned int refresh)
{
FB_Mode *modes, *mode = NULL;
unsigned int i, num_modes;
modes = fb_list_modes(&num_modes);
if (modes)
{
for (i = 0; i < num_modes; i++)
{
if ((modes[i].width == width) &&
(modes[i].height == height) &&
(!pdepth || modes[i].fb_var.bits_per_pixel == pdepth) &&
(modes[i].refresh == refresh))
{
if (pdepth) modes[i].fb_var.bits_per_pixel = pdepth;
if (ioctl(fb, FBIOPUT_VSCREENINFO, &modes[i].fb_var) == -1)
perror("ioctl FBIOPUT_VSCREENINFO");
free(modes);
return fb_getmode();
}
}
free(modes);
}
return mode;
}
FB_Mode *
fb_changedepth(FB_Mode *cur_mode, unsigned int pdepth)
{
cur_mode->fb_var.bits_per_pixel = pdepth;
if (ioctl(fb, FBIOPUT_VSCREENINFO, &cur_mode->fb_var) == -1)
perror("ioctl FBIOPUT_VSCREENINFO");
free(cur_mode);
return fb_getmode();
}
FB_Mode *
fb_changeres(FB_Mode *cur_mode, unsigned int width, unsigned int height, unsigned int refresh)
{
FB_Mode *modes;
unsigned int i, num_modes;
modes = fb_list_modes(&num_modes);
if (modes)
{
for (i = 0; i < num_modes; i++)
{
if ((modes[i].width == width) &&
(modes[i].height == height) &&
(modes[i].refresh == refresh))
{
modes[i].fb_var.bits_per_pixel = cur_mode->depth;
if (ioctl(fb, FBIOPUT_VSCREENINFO, &modes[i].fb_var) == -1)
perror("ioctl FBIOPUT_VSCREENINFO");
free(modes);
free(cur_mode);
return fb_getmode();
}
}
free(modes);
}
return cur_mode;
}
FB_Mode *
fb_changemode(FB_Mode *cur_mode, unsigned int width, unsigned int height, unsigned int pdepth, unsigned int refresh)
{
FB_Mode *modes;
unsigned int i, num_modes;
modes = fb_list_modes(&num_modes);
if (modes)
{
for (i = 0; i < num_modes; i++)
{
if ((modes[i].width == width) &&
(modes[i].height == height) &&
(!pdepth || modes[i].fb_var.bits_per_pixel == pdepth) &&
(modes[i].refresh == refresh))
{
if (pdepth) modes[i].fb_var.bits_per_pixel = pdepth;
if (ioctl(fb, FBIOPUT_VSCREENINFO, &modes[i].fb_var) == -1)
perror("ioctl FBIOPUT_VSCREENINFO");
free(modes);
free(cur_mode);
return fb_getmode();
}
}
free(modes);
}
return cur_mode;
}
FB_Mode *
fb_getmode(void)
{
FB_Mode *mode = NULL;
int hpix, lines, clockrate;
mode = malloc(sizeof(FB_Mode));
/* look what we have now ... */
if (ioctl(fb, FBIOGET_VSCREENINFO, &mode->fb_var) == -1)
{
perror("ioctl FBIOGET_VSCREENINFO");
free(mode);
return NULL;
}
mode->width = mode->fb_var.xres_virtual;
mode->height = mode->fb_var.yres_virtual;
hpix =
mode->fb_var.left_margin +
mode->fb_var.xres +
mode->fb_var.right_margin +
mode->fb_var.hsync_len;
lines =
mode->fb_var.upper_margin +
mode->fb_var.yres +
mode->fb_var.lower_margin +
mode->fb_var.vsync_len;
if (mode->fb_var.pixclock > 0)
clockrate = 1000000 / mode->fb_var.pixclock;
else
clockrate = 0;
if ((lines > 0) && (hpix > 0))
mode->refresh = clockrate * 1000000 / (lines * hpix);
switch (mode->fb_var.bits_per_pixel)
{
case 1:
bpp = 1;
depth = 1;
break;
case 4:
bpp = 1;
depth = 4;
break;
case 8:
bpp = 1;
depth = 8;
break;
case 15:
case 16:
if (mode->fb_var.green.length == 6)
depth = 16;
else
depth = 15;
bpp = 2;
break;
case 24:
depth = 24;
bpp = mode->fb_var.bits_per_pixel / 8;
break;
case 32:
depth = 32;
bpp = mode->fb_var.bits_per_pixel / 8;
break;
default:
ERR("Cannot handle framebuffer of depth %i",
mode->fb_var.bits_per_pixel);
fb_cleanup();
free(mode);
return NULL;
}
mode->depth = depth;
mode->bpp = bpp;
if (mode->depth == 8) fb_init_palette_332(mode);
else fb_init_palette_linear(mode);
return mode;
}
void
fb_freemode(FB_Mode *mode)
{
free(mode);
}
/* XXX: unused
static void
fb_setvt(int vtno)
{
struct vt_stat vts;
char vtname[32];
int vtfd;
if (vtno < 0)
{
if ((ioctl(tty,VT_OPENQRY, &vtno) == -1))
{
perror("ioctl VT_OPENQRY");
return;
}
if (vtno <= 0 )
{
perror("ioctl VT_OPENQRY vtno <= 0");
return;
}
}
vtno &= 0xff;
sprintf(vtname, "/dev/tty%i", vtno);
if (chown(vtname, getuid(), getgid()) != 0)
{
vtfd = 0; // do nothing - don't worry about chown
}
if (access(vtname,R_OK | W_OK) == -1)
{
CRI("Access %s: %s",vtname,strerror(errno));
return;
}
vtfd = open(vtname,O_RDWR);
if (ioctl(tty, VT_GETSTATE, &vts) == -1)
{
perror("ioctl VT_GETSTATE");
close(vtfd);
return;
}
orig_vt_no = vts.v_active;
close(vtfd);
#if 0
if (ioctl(tty, VT_ACTIVATE, vtno) == -1)
{
perror("ioctl VT_ACTIVATE");
exit(1);
}
if (ioctl(tty, VT_WAITACTIVE, vtno) == -1)
{
perror("ioctl VT_WAITACTIVE");
exit(1);
}
#endif
}
*/
void
fb_init(int vt EINA_UNUSED, int device)
{
char dev[PATH_MAX];
tty = -1;
#if 0
if (vt != 0) fb_setvt(vt);
#endif
if (
#if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
(getuid() == geteuid()) &&
#endif
(getenv("EVAS_FB_DEV")))
{
eina_strlcpy(dev, getenv("EVAS_FB_DEV"), sizeof(dev));
fb = open(dev, O_RDWR);
}
else
{
sprintf(dev, "/dev/fb/%i", device);
fb = open(dev, O_RDWR);
if ( fb == -1 )
{
sprintf(dev, "/dev/fb%i", device);
fb = open(dev, O_RDWR);
}
}
if (fb == -1)
{
CRI("open %s: %s", dev, strerror(errno));
fb_cleanup();
return;
}
if (ioctl(fb, FBIOGET_VSCREENINFO, &fb_ovar) == -1)
{
perror("ioctl FBIOGET_VSCREENINFO");
return;
}
if (ioctl(fb, FBIOGET_FSCREENINFO, &fb_fix) == -1)
{
perror("ioctl FBIOGET_FSCREENINFO");
return;
}
if ((fb_ovar.bits_per_pixel == 8) ||
(fb_fix.visual == FB_VISUAL_DIRECTCOLOR))
{
if (ioctl(fb,FBIOGETCMAP , &ocmap) == -1)
{
perror("ioctl FBIOGETCMAP");
return;
}
}
#if 0
if (isatty(0))
tty = 0;
else if ((tty = open("/dev/tty",O_RDWR)) == -1)
{
CITICAL("open %s: %s", "/dev/tty", strerror(errno));
return;
}
if (tty >= 0)
{
if (ioctl(tty, KDGETMODE, &kd_mode) == -1)
{
perror("ioctl KDGETMODE");
return;
}
if (ioctl(tty, VT_GETMODE, &vt_omode) == -1)
{
perror("ioctl VT_GETMODE");
return;
}
}
#endif
}
int
fb_postinit(FB_Mode *mode)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(mode, -1);
if (fb < 0)
return -1;
if (ioctl(fb,FBIOGET_FSCREENINFO, &fb_fix) == -1)
{
perror("ioctl FBIOGET_FSCREENINFO");
fb_cleanup();
return -1;
}
if (fb_fix.type != FB_TYPE_PACKED_PIXELS)
{
CRI("can handle only packed pixel frame buffers");
fb_cleanup();
return -1;
}
mode->mem_offset = (unsigned)(fb_fix.smem_start) & (getpagesize()-1);
mode->mem = (unsigned char *)mmap(NULL, fb_fix.smem_len + mode->mem_offset,
PROT_WRITE | PROT_READ, MAP_SHARED, fb, 0);
if (mode->mem == MAP_FAILED)
{
perror("mmap");
fb_cleanup();
return -1;
}
/* move viewport to upper left corner */
if ((mode->fb_var.xoffset != 0) || (mode->fb_var.yoffset != 0))
{
mode->fb_var.xoffset = 0;
mode->fb_var.yoffset = 0;
if (ioctl(fb, FBIOPAN_DISPLAY, &(mode->fb_var)) == -1)
{
perror("ioctl FBIOPAN_DISPLAY");
fb_cleanup();
return -1;
}
}
#if 0
if (tty >= 0)
{
if (ioctl(tty,KDSETMODE, KD_GRAPHICS) == -1)
{
perror("ioctl KDSETMODE");
fb_cleanup();
return -1;
}
}
#endif
mode->fb_fd = fb;
return fb;
}
void
fb_cleanup(void)
{
if (fb < 0) return;
/* restore console */
if (ioctl(fb, FBIOPUT_VSCREENINFO, &fb_ovar) == -1)
perror("ioctl FBIOPUT_VSCREENINFO");
if (ioctl(fb, FBIOGET_FSCREENINFO, &fb_fix) == -1)
perror("ioctl FBIOGET_FSCREENINFO");
if ((fb_ovar.bits_per_pixel == 8) ||
(fb_fix.visual == FB_VISUAL_DIRECTCOLOR))
{
if (ioctl(fb, FBIOPUTCMAP, &ocmap) == -1)
perror("ioctl FBIOPUTCMAP");
}
close(fb);
fb = -1;
if (tty >= 0)
{
if (ioctl(tty, KDSETMODE, kd_mode) == -1)
perror("ioctl KDSETMODE");
if (ioctl(tty, VT_SETMODE, &vt_omode) == -1)
perror("ioctl VT_SETMODE");
#if 0
if ((ioctl(tty, VT_ACTIVATE, orig_vt_no) == -1) && (orig_vt_no))
perror("ioctl VT_ACTIVATE");
#endif
if (tty > 0) /* don't close if got from isatty(0) */
close(tty);
}
tty = -1;
}