/** ** vd_lnxfb.c ---- Linux framebuffer driver ** ** Copyright (c) 2001 Mariano Alvarez Fernandez ** [e-mail: malfer@telefonica.net] ** ** This file is part of the GRX graphics library. ** ** The GRX graphics library is free software; you can redistribute it ** and/or modify it under some conditions; see the "copying.grx" file ** for details. ** ** This library is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** ** Contributions by Josu Onandia (jonandia@fagorautomation.es) 13/12/2002 ** - Added the 8bpp paletted mode. ** Modifications by Mariano Alvarez Fernandez 21/12/2002 ** - Added function to change virtual terminals, _SwitchConsoleLnxfbDriver ** to be called from the input driver. ** - Some cleanups, now the text screen shows ok on exit. ** Modifications by Mariano Alvarez Fernandez 1/3/2002 ** - Added code to catch a signal when user wants to change virtual ** terminals. The driver sets _lnxfb_waiting_to_switch_console and the ** input driver must calls _LnxfbSwitchConsoleAndWait then. ** - _SwitchConsoleLnxfbDriver renamed to _LnxfbSwitchConsoleVt, not used, ** is here only for possible future use. ** Modifications by Mariano Alvarez Fernandez 30/6/2017 * - Added 32 bpp video mode **/ #include #include #include #include #include #include #include #include #include "libgrx.h" #include "grdriver.h" #include "arith.h" #define NUM_MODES 80 /* max # of supported modes */ #define NUM_EXTS 15 /* max # of mode extensions */ static int initted = -1; static int fbfd = -1; static int ttyfd = -1; static struct fb_fix_screeninfo fbfix; static struct fb_var_screeninfo fbvar; static char *fbuffer = NULL; static int ingraphicsmode = 0; int _lnxfb_waiting_to_switch_console = 0; static int detect(void) { char *fbname; char *default_fbname = "/dev/fb0"; if (initted < 0) { initted = 0; fbname = getenv("FRAMEBUFFER"); if (fbname == NULL) fbname = default_fbname; fbfd = open(fbname, O_RDWR); if (fbfd == -1) return FALSE; ioctl(fbfd, FBIOGET_FSCREENINFO, &fbfix); ioctl(fbfd, FBIOGET_VSCREENINFO, &fbvar); if (fbfix.type != FB_TYPE_PACKED_PIXELS) return FALSE; ttyfd = open("/dev/tty", O_RDONLY); initted = 1; } return ((initted > 0) ? TRUE : FALSE); } static void reset(void) { struct vt_mode vtm; if (fbuffer) { memset(fbuffer, 0, fbvar.yres * fbfix.line_length); munmap(fbuffer, fbfix.smem_len); fbuffer = NULL; } if (fbfd != -1) { close(fbfd); fbfd = -1; } if (ttyfd > -1) { ioctl(ttyfd, KDSETMODE, KD_TEXT); vtm.mode = VT_AUTO; vtm.relsig = 0; vtm.acqsig = 0; ioctl(ttyfd, VT_SETMODE, &vtm); signal(SIGUSR1, SIG_IGN); close(ttyfd); ttyfd = -1; ingraphicsmode = 0; } initted = -1; } void _LnxfbSwitchToConsoleVt(unsigned short vt) { struct vt_stat vtst; unsigned short myvt; GrContext *grc; if (!ingraphicsmode) return; if (ttyfd < 0) return; if (ioctl(ttyfd, VT_GETSTATE, &vtst) < 0) return; myvt = vtst.v_active; if (vt == myvt) return; grc = GrCreateContext(GrScreenX(), GrScreenY(), NULL, NULL); if (grc != NULL) { GrBitBlt(grc, 0, 0, GrScreenContext(), 0, 0, GrScreenX()-1, GrScreenY()-1, GrWRITE); } ioctl(ttyfd, KDSETMODE, KD_TEXT); if (ioctl(ttyfd, VT_ACTIVATE, vt) == 0) { ioctl(ttyfd, VT_WAITACTIVE, vt); ioctl(ttyfd, VT_WAITACTIVE, myvt); } ioctl(ttyfd, KDSETMODE, KD_GRAPHICS); if (grc != NULL) { GrBitBlt(GrScreenContext(), 0, 0, grc, 0, 0, GrScreenX()-1, GrScreenY()-1, GrWRITE); GrDestroyContext(grc); } } void _LnxfbSwitchConsoleAndWait(void) { struct vt_stat vtst; unsigned short myvt; GrContext *grc; _lnxfb_waiting_to_switch_console = 0; if (!ingraphicsmode) return; if (ttyfd < 0) return; if (ioctl(ttyfd, VT_GETSTATE, &vtst) < 0) return; myvt = vtst.v_active; grc = GrCreateContext(GrScreenX(), GrScreenY(), NULL, NULL); if (grc != NULL) { GrBitBlt(grc, 0, 0, GrScreenContext(), 0, 0, GrScreenX()-1, GrScreenY()-1, GrWRITE); } ioctl(ttyfd, KDSETMODE, KD_TEXT); ioctl(ttyfd, VT_RELDISP, 1); ioctl(ttyfd, VT_WAITACTIVE, myvt); ioctl(ttyfd, KDSETMODE, KD_GRAPHICS); if (grc != NULL) { GrBitBlt(GrScreenContext(), 0, 0, grc, 0, 0, GrScreenX()-1, GrScreenY()-1, GrWRITE); GrDestroyContext(grc); } } void _LnxfbRelsigHandle(int sig) { _lnxfb_waiting_to_switch_console = 1; signal(SIGUSR1, _LnxfbRelsigHandle); } static void loadcolor(int c, int r, int g, int b) { __u16 red, green, blue, transp; struct fb_cmap cmap; red = (r << 8); green = (g << 8); blue = (b << 8); transp = 0; cmap.start = c; cmap.len = 1; cmap.red = &red; cmap.green = &green; cmap.blue = &blue; cmap.transp = &transp; ioctl(fbfd, FBIOPUTCMAP, &cmap); } static int setmode(GrVideoMode * mp, int noclear) { struct vt_mode vtm; fbuffer = mp->extinfo->frame = mmap(NULL, fbfix.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0); if (fbuffer == MAP_FAILED) { fbuffer = NULL; mp->extinfo->frame = NULL; return FALSE; } if (mp->extinfo->frame && ttyfd > -1) { ioctl(ttyfd, KDSETMODE, KD_GRAPHICS); vtm.mode = VT_PROCESS; vtm.relsig = SIGUSR1; vtm.acqsig = 0; ioctl(ttyfd, VT_SETMODE, &vtm); signal(SIGUSR1, _LnxfbRelsigHandle); ingraphicsmode = 1; } if (mp->extinfo->frame && !noclear) memset(mp->extinfo->frame, 0, fbvar.yres * fbfix.line_length); return ((mp->extinfo->frame) ? TRUE : FALSE); } static int settext(GrVideoMode * mp, int noclear) { struct vt_mode vtm; if (fbuffer) { memset(fbuffer, 0, fbvar.yres * fbfix.line_length); munmap(fbuffer, fbfix.smem_len); fbuffer = NULL; } if (ttyfd > -1) { ioctl(ttyfd, KDSETMODE, KD_TEXT); vtm.mode = VT_AUTO; vtm.relsig = 0; vtm.acqsig = 0; ioctl(ttyfd, VT_SETMODE, &vtm); signal(SIGUSR1, SIG_IGN); ingraphicsmode = 0; } return TRUE; } GrVideoModeExt grtextextfb = { GR_frameText, /* frame driver */ NULL, /* frame driver override */ NULL, /* frame buffer address */ {6, 6, 6}, /* color precisions */ {0, 0, 0}, /* color component bit positions */ 0, /* mode flag bits */ settext, /* mode set */ NULL, /* virtual size set */ NULL, /* virtual scroll */ NULL, /* bank set function */ NULL, /* double bank set function */ NULL, /* color loader */ }; static GrVideoModeExt exts[NUM_EXTS]; static GrVideoMode modes[NUM_MODES] = { /* pres. bpp wdt hgt mode scan priv. &ext */ {TRUE, 4, 80, 25, 0, 160, 0, &grtextextfb}, {0} }; static int build_video_mode(GrVideoMode * mp, GrVideoModeExt * ep) { mp->present = TRUE; mp->width = fbvar.xres; mp->height = fbvar.yres; mp->lineoffset = fbfix.line_length; mp->extinfo = NULL; mp->privdata = 0; ep->drv = NULL; ep->frame = NULL; /* filled in after mode set */ ep->flags = 0; ep->setup = setmode; ep->setvsize = NULL; /* tbd */ ep->scroll = NULL; /* tbd */ ep->setbank = NULL; ep->setrwbanks = NULL; ep->loadcolor = NULL; switch (fbvar.bits_per_pixel) { case 8: if (fbfix.visual != FB_VISUAL_PSEUDOCOLOR) return FALSE; mp->bpp = 8; ep->mode = GR_frameSVGA8_LFB; ep->flags |= GR_VMODEF_LINEAR; ep->cprec[0] = fbvar.red.length; ep->cprec[1] = fbvar.green.length; ep->cprec[2] = fbvar.blue.length; ep->cpos[0] = fbvar.red.offset; ep->cpos[1] = fbvar.green.offset; ep->cpos[2] = fbvar.blue.offset; ep->loadcolor = loadcolor; break; case 15: if (fbfix.visual != FB_VISUAL_TRUECOLOR) return FALSE; mp->bpp = 15; ep->mode = GR_frameLNXFB_16; ep->flags |= GR_VMODEF_LINEAR; ep->cprec[0] = fbvar.red.length; ep->cprec[1] = fbvar.green.length; ep->cprec[2] = fbvar.blue.length; ep->cpos[0] = fbvar.red.offset; ep->cpos[1] = fbvar.green.offset; ep->cpos[2] = fbvar.blue.offset; break; case 16: if (fbfix.visual != FB_VISUAL_TRUECOLOR) return FALSE; mp->bpp = 16; ep->mode = GR_frameLNXFB_16; ep->flags |= GR_VMODEF_LINEAR; ep->cprec[0] = fbvar.red.length; ep->cprec[1] = fbvar.green.length; ep->cprec[2] = fbvar.blue.length; ep->cpos[0] = fbvar.red.offset; ep->cpos[1] = fbvar.green.offset; ep->cpos[2] = fbvar.blue.offset; break; case 24: if (fbfix.visual != FB_VISUAL_TRUECOLOR) return FALSE; mp->bpp = 24; ep->mode = GR_frameSVGA24_LFB; ep->flags |= GR_VMODEF_LINEAR; ep->cprec[0] = fbvar.red.length; ep->cprec[1] = fbvar.green.length; ep->cprec[2] = fbvar.blue.length; ep->cpos[0] = fbvar.red.offset; ep->cpos[1] = fbvar.green.offset; ep->cpos[2] = fbvar.blue.offset; break; case 32: if (fbfix.visual != FB_VISUAL_TRUECOLOR) return FALSE; mp->bpp = 32; ep->mode = (fbvar.red.offset == 16) ? GR_frameLNXFB_32L : GR_frameLNXFB_32H; ep->flags |= GR_VMODEF_LINEAR; ep->cprec[0] = fbvar.red.length; ep->cprec[1] = fbvar.green.length; ep->cprec[2] = fbvar.blue.length; ep->cpos[0] = fbvar.red.offset; ep->cpos[1] = fbvar.green.offset; ep->cpos[2] = fbvar.blue.offset; break; default: return (FALSE); } return (TRUE); } static void add_video_mode(GrVideoMode * mp, GrVideoModeExt * ep, GrVideoMode ** mpp, GrVideoModeExt ** epp) { if (*mpp < &modes[NUM_MODES]) { if (!mp->extinfo) { GrVideoModeExt *etp = &exts[0]; while (etp < *epp) { if (memcmp(etp, ep, sizeof(GrVideoModeExt)) == 0) { mp->extinfo = etp; break; } etp++; } if (!mp->extinfo) { if (etp >= &exts[NUM_EXTS]) return; sttcopy(etp, ep); mp->extinfo = etp; *epp = ++etp; } } sttcopy(*mpp, mp); (*mpp)++; } } static int init(char *options) { if (detect()) { GrVideoMode mode, *modep = &modes[1]; GrVideoModeExt ext, *extp = &exts[0]; memset(modep, 0, (sizeof(modes) - sizeof(modes[0]))); if ((build_video_mode(&mode, &ext))) { add_video_mode(&mode, &ext, &modep, &extp); } return (TRUE); } return (FALSE); } GrVideoDriver _GrVideoDriverLINUXFB = { "linuxfb", /* name */ GR_LNXFB, /* adapter type */ NULL, /* inherit modes from this driver */ modes, /* mode table */ itemsof(modes), /* # of modes */ detect, /* detection routine */ init, /* initialization routine */ reset, /* reset routine */ _gr_selectmode, /* standard mode select routine */ 0 /* no additional capabilities */ };