/*
* Linux Frame Buffer Device Configuration
*
* Copyright 1995-1999 by Geert Uytterhoeven
* (Geert.Uytterhoeven@cs.kuleuven.ac.be)
*
* --------------------------------------------------------------------------
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of the Linux
* distribution for more details.
*
* Petr Vandrovec <vandrove@vc.cvut.cz>:
* -grayscale, -rgba, -nonstd, VGA modes reporting
*
* Brad Midgley <brad@exodus.pht.com>:
* -match
*
*/
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <ctype.h>
#include <sys/stat.h>
struct file;
struct inode;
#include "fbset.h"
/*
* Default Frame Buffer Special Device Node
*/
#define DEFAULT_FRAMEBUFFER "/dev/fb0"
/*
* Default Video Mode Database File
*/
#define DEFAULT_MODEDBFILE "/etc/fb.modes"
/*
* Command Line Options
*/
static const char *ProgramName;
static int Opt_test = 0;
static int Opt_show = 0;
static int Opt_info = 0;
static int Opt_version = 0;
static int Opt_verbose = 0;
static int Opt_xfree86 = 0;
static int Opt_change = 0;
static int Opt_all = 0;
static const char *Opt_fb = NULL;
const char *Opt_modedb = DEFAULT_MODEDBFILE;
static const char *Opt_xres = NULL;
static const char *Opt_yres = NULL;
static const char *Opt_vxres = NULL;
static const char *Opt_vyres = NULL;
static const char *Opt_depth = NULL;
static const char *Opt_pixclock = NULL;
static const char *Opt_left = NULL;
static const char *Opt_right = NULL;
static const char *Opt_upper = NULL;
static const char *Opt_lower = NULL;
static const char *Opt_hslen = NULL;
static const char *Opt_vslen = NULL;
static const char *Opt_accel = NULL;
static const char *Opt_hsync = NULL;
static const char *Opt_vsync = NULL;
static const char *Opt_csync = NULL;
static const char *Opt_gsync = NULL;
static const char *Opt_extsync = NULL;
static const char *Opt_bcast = NULL;
static const char *Opt_laced = NULL;
static const char *Opt_double = NULL;
static const char *Opt_move = NULL;
static const char *Opt_step = NULL;
static const char *Opt_modename = NULL;
static const char *Opt_rgba = NULL;
static const char *Opt_nonstd = NULL;
static const char *Opt_grayscale = NULL;
static const char *Opt_matchyres = NULL;
static struct {
const char *name;
const char **value;
const int change;
} Options[] = {
{ "-fb", &Opt_fb, 0 },
{ "-db", &Opt_modedb, 0 },
{ "-xres", &Opt_xres, 1 },
{ "-yres", &Opt_yres, 1 },
{ "-vxres", &Opt_vxres, 1 },
{ "-vyres", &Opt_vyres, 1 },
{ "-depth", &Opt_depth, 1 },
{ "-nonstd", &Opt_nonstd, 1},
{ "-pixclock", &Opt_pixclock, 1 },
{ "-left", &Opt_left, 1 },
{ "-right", &Opt_right, 1 },
{ "-upper", &Opt_upper, 1 },
{ "-lower", &Opt_lower, 1 },
{ "-hslen", &Opt_hslen, 1 },
{ "-vslen", &Opt_vslen, 1 },
{ "-accel", &Opt_accel, 1 },
{ "-hsync", &Opt_hsync, 1 },
{ "-vsync", &Opt_vsync, 1 },
{ "-csync", &Opt_csync, 1 },
{ "-gsync", &Opt_gsync, 1 },
{ "-extsync", &Opt_extsync, 1 },
{ "-bcast", &Opt_bcast, 1 },
{ "-laced", &Opt_laced, 1 },
{ "-double", &Opt_double, 1 },
{ "-move", &Opt_move, 1 },
{ "-step", &Opt_step, 1 },
{ "-rgba", &Opt_rgba, 1 },
{ "-grayscale", &Opt_grayscale, 1 },
{ NULL, NULL, 0 }
};
/*
* Video Mode Database
*/
struct VideoMode *VideoModes = NULL;
/*
* Hardware Text Modes
*/
static struct textentry {
__u32 id;
const char *name;
} Textmodes[] = {
{ FB_AUX_TEXT_MDA, "Monochrome text" },
{ FB_AUX_TEXT_CGA, "CGA/EGA/VGA Color text" },
{ FB_AUX_TEXT_S3_MMIO, "S3 MMIO fasttext" },
{ FB_AUX_TEXT_MGA_STEP16, "MGA Millennium I step 16 text" },
{ FB_AUX_TEXT_MGA_STEP8, "MGA step 8 text" },
};
static struct textentry VGAModes[] = {
#ifdef HAVE_FB_AUX_VGA_PLANES_VGA4
{ FB_AUX_VGA_PLANES_VGA4, "VGA 16 colors in 4 planes" },
#endif
#ifdef HAVE_FB_AUX_VGA_PLANES_CFB4
{ FB_AUX_VGA_PLANES_CFB4, "VGA 16 colors in 1 plane" },
#endif
#ifdef HAVE_FB_AUX_VGA_PLANES_CFB8
{ FB_AUX_VGA_PLANES_CFB8, "VGA 256 colors in 4 planes" },
#endif
/* last entry has name == NULL */
{ 0, NULL}
};
/*
* Hardware Accelerators
*/
static struct accelentry {
__u32 id;
const char *name;
} Accelerators[] = {
{ FB_ACCEL_NONE, "No" },
{ FB_ACCEL_ATARIBLITT, "Atari Blitter" },
{ FB_ACCEL_AMIGABLITT, "Amiga Blitter" },
{ FB_ACCEL_S3_TRIO64, "S3 Trio64" },
{ FB_ACCEL_NCR_77C32BLT, "NCR 77C32BLT" },
{ FB_ACCEL_S3_VIRGE, "S3 ViRGE" },
{ FB_ACCEL_ATI_MACH64GX, "ATI Mach64GX" },
{ FB_ACCEL_DEC_TGA, "DEC 21030 TGA" },
{ FB_ACCEL_ATI_MACH64CT, "ATI Mach64CT" },
{ FB_ACCEL_ATI_MACH64VT, "ATI Mach64VT" },
{ FB_ACCEL_ATI_MACH64GT, "ATI Mach64GT" },
{ FB_ACCEL_SUN_CREATOR, "Sun Creator/Creator3D" },
{ FB_ACCEL_SUN_CGSIX, "Sun cg6" },
{ FB_ACCEL_SUN_LEO, "Sun leo/zx" },
{ FB_ACCEL_IMS_TWINTURBO, "IMS Twin Turbo" },
{ FB_ACCEL_3DLABS_PERMEDIA2, "3Dlabs Permedia 2" },
{ FB_ACCEL_MATROX_MGA2064W, "Matrox MGA2064W (Millennium)" },
{ FB_ACCEL_MATROX_MGA1064SG, "Matrox MGA1064SG (Mystique)" },
{ FB_ACCEL_MATROX_MGA2164W, "Matrox MGA2164W (Millennium II)" },
{ FB_ACCEL_MATROX_MGA2164W_AGP, "Matrox MGA2164W (Millennium II AGP)" },
{ FB_ACCEL_MATROX_MGAG100, "Matrox G100 (Productiva G100)" },
{ FB_ACCEL_MATROX_MGAG200, "Matrox G200 (Millennium, Mystique)" },
{ FB_ACCEL_SUN_CG14, "Sun cg14" },
{ FB_ACCEL_SUN_BWTWO, "Sun bw2" },
{ FB_ACCEL_SUN_CGTHREE, "Sun cg3" },
{ FB_ACCEL_SUN_TCX, "Sun tcx" },
{ FB_ACCEL_MATROX_MGAG400, "Matrox G400" },
};
/*
* Current Video Mode
*/
struct VideoMode Current;
/*
* Function Prototypes
*/
int OpenFrameBuffer(const char *name);
void CloseFrameBuffer(int fh);
int GetVarScreenInfo(int fh, struct fb_var_screeninfo *var);
int SetVarScreenInfo(int fh, struct fb_var_screeninfo *var);
int GetFixScreenInfo(int fh, struct fb_fix_screeninfo *fix);
void ConvertFromVideoMode(const struct VideoMode *vmode,
struct fb_var_screeninfo *var);
void ConvertToVideoMode(const struct fb_var_screeninfo *var,
struct VideoMode *vmode);
static int atoboolean(const char *var);
void ReadModeDB(void);
struct VideoMode *FindVideoMode(const char *name);
static void ModifyVideoMode(struct VideoMode *vmode);
static void DisplayVModeInfo(struct VideoMode *vmode);
static void DisplayFBInfo(struct fb_fix_screeninfo *fix);
static int FillScanRates(struct VideoMode *vmode);
static void Usage(void);
int main(int argc, char *argv[]);
extern void Con_Printf (const char *fmt, ...);
#define Die Con_Printf
#define puts(s) Con_Printf("%s\n",(s))
#define printf Con_Printf
/*
* Open the Frame Buffer Device
*/
int OpenFrameBuffer(const char *name)
{
int fh;
if (Opt_verbose)
printf("Opening frame buffer device `%s'\n", name);
if ((fh = open(name, O_RDWR)) == -1)
Die("open %s: %s\n", name, strerror(errno));
return fh;
}
/*
* Close the Frame Buffer Device
*/
void CloseFrameBuffer(int fh)
{
close(fh);
}
/*
* Get the Variable Part of the Screen Info
*/
int GetVarScreenInfo(int fh, struct fb_var_screeninfo *var)
{
if (ioctl(fh, FBIOGET_VSCREENINFO, var)) {
Die("ioctl FBIOGET_VSCREENINFO: %s\n", strerror(errno));
return -1;
}
return 0;
}
/*
* Set (and Get) the Variable Part of the Screen Info
*/
int SetVarScreenInfo(int fh, struct fb_var_screeninfo *var)
{
if (ioctl(fh, FBIOPUT_VSCREENINFO, var)) {
Die("ioctl FBIOPUT_VSCREENINFO: %s\n", strerror(errno));
return -1;
}
return 0;
}
/*
* Get the Fixed Part of the Screen Info
*/
int GetFixScreenInfo(int fh, struct fb_fix_screeninfo *fix)
{
if (ioctl(fh, FBIOGET_FSCREENINFO, fix)) {
Die("ioctl FBIOGET_FSCREENINFO: %s\n", strerror(errno));
return -1;
}
return 0;
}
/*
* Conversion Routines
*/
void ConvertFromVideoMode(const struct VideoMode *vmode,
struct fb_var_screeninfo *var)
{
memset(var, 0, sizeof(struct fb_var_screeninfo));
var->xres = vmode->xres;
var->yres = vmode->yres;
var->xres_virtual = vmode->vxres;
var->yres_virtual = vmode->vyres;
var->bits_per_pixel = vmode->depth;
var->nonstd = vmode->nonstd;
if (Opt_test)
var->activate = FB_ACTIVATE_TEST;
else
var->activate = FB_ACTIVATE_NOW;
if (Opt_all)
var->activate = FB_ACTIVATE_ALL;
var->accel_flags = vmode->accel_flags;
var->pixclock = vmode->pixclock;
var->left_margin = vmode->left;
var->right_margin = vmode->right;
var->upper_margin = vmode->upper;
var->lower_margin = vmode->lower;
var->hsync_len = vmode->hslen;
var->vsync_len = vmode->vslen;
if (vmode->hsync == HIGH)
var->sync |= FB_SYNC_HOR_HIGH_ACT;
if (vmode->vsync == HIGH)
var->sync |= FB_SYNC_VERT_HIGH_ACT;
if (vmode->csync == HIGH)
var->sync |= FB_SYNC_COMP_HIGH_ACT;
if (vmode->gsync == HIGH)
var->sync |= FB_SYNC_ON_GREEN;
if (vmode->extsync == TRUE)
var->sync |= FB_SYNC_EXT;
if (vmode->bcast == TRUE)
var->sync |= FB_SYNC_BROADCAST;
if (vmode->laced == TRUE)
var->vmode = FB_VMODE_INTERLACED;
else if (vmode->dblscan == TRUE)
var->vmode = FB_VMODE_DOUBLE;
else
var->vmode = FB_VMODE_NONINTERLACED;
var->vmode |= FB_VMODE_CONUPDATE;
var->red.length = vmode->red.length;
var->red.offset = vmode->red.offset;
var->green.length = vmode->green.length;
var->green.offset = vmode->green.offset;
var->blue.length = vmode->blue.length;
var->blue.offset = vmode->blue.offset;
var->transp.length = vmode->transp.length;
var->transp.offset = vmode->transp.offset;
var->grayscale = vmode->grayscale;
}
void ConvertToVideoMode(const struct fb_var_screeninfo *var,
struct VideoMode *vmode)
{
vmode->name = NULL;
vmode->xres = var->xres;
vmode->yres = var->yres;
vmode->vxres = var->xres_virtual;
vmode->vyres = var->yres_virtual;
vmode->depth = var->bits_per_pixel;
vmode->nonstd = var->nonstd;
vmode->accel_flags = var->accel_flags;
vmode->pixclock = var->pixclock;
vmode->left = var->left_margin;
vmode->right = var->right_margin;
vmode->upper = var->upper_margin;
vmode->lower = var->lower_margin;
vmode->hslen = var->hsync_len;
vmode->vslen = var->vsync_len;
vmode->hsync = var->sync & FB_SYNC_HOR_HIGH_ACT ? HIGH : LOW;
vmode->vsync = var->sync & FB_SYNC_VERT_HIGH_ACT ? HIGH : LOW;
vmode->csync = var->sync & FB_SYNC_COMP_HIGH_ACT ? HIGH : LOW;
vmode->gsync = var->sync & FB_SYNC_ON_GREEN ? TRUE : FALSE;
vmode->extsync = var->sync & FB_SYNC_EXT ? TRUE : FALSE;
vmode->bcast = var->sync & FB_SYNC_BROADCAST ? TRUE : FALSE;
vmode->grayscale = var->grayscale;
vmode->laced = FALSE;
vmode->dblscan = FALSE;
switch (var->vmode & FB_VMODE_MASK) {
case FB_VMODE_INTERLACED:
vmode->laced = TRUE;
break;
case FB_VMODE_DOUBLE:
vmode->dblscan = TRUE;
break;
}
vmode->red.length = var->red.length;
vmode->red.offset = var->red.offset;
vmode->green.length = var->green.length;
vmode->green.offset = var->green.offset;
vmode->blue.length = var->blue.length;
vmode->blue.offset = var->blue.offset;
vmode->transp.length = var->transp.length;
vmode->transp.offset = var->transp.offset;
FillScanRates(vmode);
}
static int atoboolean(const char *var)
{
int value = 0;
if (!strcasecmp(var, "false") || !strcasecmp(var, "low") ||
!strcasecmp(var, "no") || !strcasecmp(var, "off") ||
!strcmp(var, "0"))
value = 0;
else if (!strcasecmp(var, "true") || !strcasecmp(var, "high") ||
!strcasecmp(var, "yes") || !strcasecmp(var, "on") ||
!strcmp(var, "1"))
value = 1;
else
Die("Invalid value `%s'\n", var);
return value;
}
void AddVideoMode(const struct VideoMode *vmode)
{
struct VideoMode *vmode2;
if (FindVideoMode(vmode->name))
Die("%s:%d: Duplicate mode name `%s'\n", Opt_modedb, line,
vmode->name);
vmode2 = malloc(sizeof(struct VideoMode));
*vmode2 = *vmode;
if (!FillScanRates(vmode2))
Die("%s:%d: Bad video mode `%s'\n", Opt_modedb, line, vmode2->name);
vmode2->next = VideoModes;
VideoModes = vmode2;
}
/*
* Read the Video Mode Database
*/
void ReadModeDB(void)
{
if (Opt_verbose)
printf("Reading mode database from file `%s'\n", Opt_modedb);
if (!(yyin = fopen(Opt_modedb, "r"))) {
Die("fopen %s: %s\n", Opt_modedb, strerror(errno));
return;
}
yyparse();
fclose(yyin);
}
static void getColor(struct color *color, const char** opt)
{
char* ptr;
color->length = 0;
color->offset = 0;
ptr = (char*)(*opt);
if (!ptr)
return;
color->length = strtoul(ptr, &ptr, 0);
if (!ptr)
return;
if (*ptr == '/')
color->offset = strtoul(ptr+1, &ptr, 0);
if (ptr) {
while (*ptr && isspace(*ptr))
ptr++;
if (*ptr == ',') {
ptr++;
} else if (*ptr)
Die("Bad RGBA syntax, rL/rO,gL/gO,bL/bO,tL/tO or rL,gL,bL,tL\n");
}
*opt = ptr;
return;
}
void makeRGBA(struct VideoMode *vmode, const char* opt)
{
getColor(&vmode->red, &opt);
getColor(&vmode->green, &opt);
getColor(&vmode->blue, &opt);
getColor(&vmode->transp, &opt);
}
/*
* Find a Video Mode
*/
struct VideoMode *FindVideoMode(const char *name)
{
struct VideoMode *vmode;
for (vmode = VideoModes; vmode; vmode = vmode->next)
if (!strcmp(name, vmode->name))
break;
return vmode;
}
/*
* Modify a Video Mode
*/
static void ModifyVideoMode(struct VideoMode *vmode)
{
u_int hstep = 8, vstep = 2;
if (Opt_xres)
vmode->xres = strtoul(Opt_xres, NULL, 0);
if (Opt_yres)
vmode->yres = strtoul(Opt_yres, NULL, 0);
if (Opt_vxres)
vmode->vxres = strtoul(Opt_vxres, NULL, 0);
if (Opt_vyres)
vmode->vyres = strtoul(Opt_vyres, NULL, 0);
if (Opt_depth)
vmode->depth = strtoul(Opt_depth, NULL, 0);
if (Opt_nonstd)
vmode->nonstd = strtoul(Opt_nonstd, NULL, 0);
if (Opt_accel)
vmode->accel_flags = atoboolean(Opt_accel) ? FB_ACCELF_TEXT : 0;
if (Opt_pixclock)
vmode->pixclock = strtoul(Opt_pixclock, NULL, 0);
if (Opt_left)
vmode->left = strtoul(Opt_left, NULL, 0);
if (Opt_right)
vmode->right = strtoul(Opt_right, NULL, 0);
if (Opt_upper)
vmode->upper = strtoul(Opt_upper, NULL, 0);
if (Opt_lower)
vmode->lower = strtoul(Opt_lower, NULL, 0);
if (Opt_hslen)
vmode->hslen = strtoul(Opt_hslen, NULL, 0);
if (Opt_vslen)
vmode->vslen = strtoul(Opt_vslen, NULL, 0);
if (Opt_hsync)
vmode->hsync = atoboolean(Opt_hsync);
if (Opt_vsync)
vmode->vsync = atoboolean(Opt_vsync);
if (Opt_csync)
vmode->csync = atoboolean(Opt_csync);
if (Opt_gsync)
vmode->gsync = atoboolean(Opt_gsync);
if (Opt_extsync)
vmode->extsync = atoboolean(Opt_extsync);
if (Opt_bcast)
vmode->bcast = atoboolean(Opt_bcast);
if (Opt_laced)
vmode->laced = atoboolean(Opt_laced);
if (Opt_double)
vmode->dblscan = atoboolean(Opt_double);
if (Opt_grayscale)
vmode->grayscale = atoboolean(Opt_grayscale);
if (Opt_step)
hstep = vstep = strtoul(Opt_step, NULL, 0);
if (Opt_matchyres)
vmode->vyres = vmode->yres;
if (Opt_move) {
if (!strcasecmp(Opt_move, "left")) {
if (hstep > vmode->left)
Die("The left margin cannot be negative\n");
vmode->left -= hstep;
vmode->right += hstep;
} else if (!strcasecmp(Opt_move, "right")) {
if (hstep > vmode->right)
Die("The right margin cannot be negative\n");
vmode->left += hstep;
vmode->right -= hstep;
} else if (!strcasecmp(Opt_move, "up")) {
if (vstep > vmode->upper)
Die("The upper margin cannot be negative\n");
vmode->upper -= vstep;
vmode->lower += vstep;
} else if (!strcasecmp(Opt_move, "down")) {
if (vstep > vmode->lower)
Die("The lower margin cannot be negative\n");
vmode->upper += vstep;
vmode->lower -= vstep;
} else
Die("Invalid direction `%s'\n", Opt_move);
}
if (Opt_rgba) {
makeRGBA(vmode, Opt_rgba);
}
if (!FillScanRates(vmode))