Linux framebuffer not updating when using KD_GRAPHICS - c

I've been trying to do some old-school drawing on the Linux framebuffer. I'm aware this is not the "modern" way to do things but exploring it out of curiosity. I am confused that my screen does not seem to update as expected when using KD_GRAPHICS mode.
Running the following program from the console to paint the screen white, the framebuffer will display a section of white on the screen, then stop updating for the duration of the sleep(), then briefly flash white before returning to KD_TEXT mode. If I comment out the KDSETMODE changes, then the framebuffer is painted white immediately except for the cursor being visible (which I don't want) and stays white for the expected 5 seconds. Can someone enlighten me why I am not seeing screen updates with KD_GRAPHCIS?
Machine is running Ubuntu 20.04 with a 5.13.0-52-generic kernel.
#include <linux/fb.h>
#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/kd.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <unistd.h>
uint32_t pixel_color(uint8_t r, uint8_t g, uint8_t b, struct fb_var_screeninfo *vinfo)
{
return (r<<vinfo->red.offset) | (g<<vinfo->green.offset) | (b<<vinfo->blue.offset);
}
int main()
{
ioctl(0,KDSETMODE,KD_GRAPHICS);
struct fb_fix_screeninfo finfo;
struct fb_var_screeninfo vinfo;
int fb_fd = open("/dev/fb0",O_RDWR);
ioctl(fb_fd, FBIOGET_VSCREENINFO, &vinfo);
ioctl(fb_fd, FBIOGET_FSCREENINFO, &finfo);
long screensize = vinfo.yres_virtual * finfo.line_length;
uint8_t *fbp = mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, (off_t)0);
int x,y;
for (x=0;x<vinfo.xres;x++)
for (y=0;y<vinfo.yres;y++)
{
long location = x * (vinfo.bits_per_pixel/8) + y * finfo.line_length;
*((uint32_t*)(fbp + location)) = pixel_color(0xFF,0xFF,0xFF, &vinfo);
}
sleep(5);
ioctl(0,KDSETMODE,KD_TEXT);
return 0;
}

Related

How to read and display file data using int86 function with REGS struct for 8086 in C-Language

I have a text file with some content I have to move the cursor in from relative to the BOF and display its content on the screen using int 21h/42h.
here is the code I am working on. I am using windows 98 (16-bit DOS)in VM and it's part of my system programming assignment so I have to use it tried in Turbo c++ with DOSBox but It has some issues.
on printing buff it displays random values
Code
#include <stdio.h>
#include <conio.h>
#include <fcntl.h>
#include <bios.h>
#include <dos.h>
unsigned int handle;
char buff[50];
void main(){
union REGS regs; // set pointer
union REGS regs_r; // read file
handle = open("text.txt", O_RDONLY);
// set pointer to BOF (Begenning of File)
regs.x.bx = handle;
regs.h.ah = 0x42; // LSEEK
regs.h.al = 0x00 // Mode (0) BOF
regs.x.cx = 0;
regs.x.dx = 0;
int86(0x21, &regs, &regs);
// read the file
regs_r.x.bx = handle;
regs_r.x.cx = 0x07; Bytes to read ?
regs_r.h.ah = 0x3fh;
regs_r.x.dx = (unsigned int) buff; // buffer for data
int86(0x21, &regs_r, &regs_r);
printf("DATA : %c", buff);
getch();
clrscr();
}
here are some reference links
8086 int
21h/42h
asm move File
Pointer
any help will be appreciated.
To get the data-segment you could try this way:
Declare a far pointer and assign to it the address of your buffer and recast it.
char buffer[100];
char far *ptr = (char far *)&buffer;
now take the 16 most significant bits from the pointer paying attention to the complement of the sign
unsigned long addr_value = (unsigned long)ptr;
unsigned int data_segment = (unsigned int)(addr_value >> 16);
It should work.

Using the gotoxy() function to center X coordinate

I want to write something using printf while also centering the x coordinate and y=0.
How can I center the x coordinate? For example someone might have their compiler window open in fullscreen and others might not? I want the text in the middle. Right now x is assigned a random value (50)
#include <stdio.h>
#include <conio.h>
int main()
{
gotoxy(50,0);
printf("Test");
return 0;
}
I'm just using an online compiler right now. onlinegdb.com Was thinking if there was a way to center the x so that it's the same in every compiler.
What is possible or not isn't determined by the compiler you are using, but by the platform and the ammount of code you are prepared to write.
Standard C has no idea of consoles, windows and other platform dependent stuff. If you want to get to know something about your consoles properties you have to ask the console/operating system. There are also libraries like ncurses for POSIX that allowes different terminals POSIX systems can run on to be treated uniformly.
An implementation of the ncurses-library that is available for DOS, OS/2, Win32, X11 and SDL is PDCurses. It can be used to write platform agnostic code.
But since you mentioned that your platform is windows, here is a solution that uses only the WinAPI:
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <windows.h>
COORD get_console_dimensions(void)
{
CONSOLE_SCREEN_BUFFER_INFO csbi;
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
COORD dimensions = { csbi.srWindow.Right - csbi.srWindow.Left,
csbi.srWindow.Bottom - csbi.srWindow.Top };
return dimensions;
}
COORD get_console_cursor_pos(void)
{
CONSOLE_SCREEN_BUFFER_INFO csbi;
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
return csbi.dwCursorPosition;
}
void gotoxy(short x, short y)
{
COORD pos = { x, y };
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
}
void puts_centered(char const *str)
{
size_t length = strlen(str);
short x = (short)(get_console_dimensions().X - length) / 2;
gotoxy(x, get_console_cursor_pos().Y);
puts(str);
}
int main(void)
{
puts_centered("Hello, World!");
}
Using ncurses the same can be achieved (also works with PDCurses, include <curses.h> instead of <ncurses.h>):
#include <string.h>
#include <ncurses.h>
int main(void)
{
initscr();
int max_x = getmaxx(stdscr);
int y, x;
getyx(stdscr, y, x);
char const *str = "Hello, World!\n";
mvaddstr(y, (max_x - strlen(str)) / 2, str);
refresh();
// endwin(); // *)
}
Live: https://onlinegdb.com/HkIpXBUim
Please note that OnlineGDBs support for ncurses with its "terminal" is broken. getyx() won't tell the real width of its console.
*) Documentation says you should call endwin() before exiting your program. If you do so with OnlineGDB you won't get any visible output at all from OnlineGDB. Only if you click the "Copy output to clipboard"-button and view the copied text you'll see the ANSI escape sequences produced by ncurses.

mbed uvisor and EthernetInterface overflowed

I am quite new to mbed and uvisor so maybe my problem is about understanding how things work. I have a NXP FRDM-K64F board where I am trying to learn about mbed and uvisor. I have succesfully compiled an run some basic examples of tasks running on different boxes. I am trying to connect to the net one of the boxes in uvisor but something is not working correctly.
This is the main file code:
#include "uvisor-lib/uvisor-lib.h"
#include "mbed.h"
#include "main-hw.h"
/* Create ACLs for main box. */
MAIN_ACL(g_main_acl);
/* Enable uVisor. */
UVISOR_SET_MODE_ACL(UVISOR_ENABLED, g_main_acl);
UVISOR_SET_PAGE_HEAP(8 * 1024, 5);
int main(void)
{
printf("----Eup---------\r\n");
DigitalOut led(MAIN_LED);
while (1) {
printf("taka\r\n");
led = !led;
/* Blink once per second. */
Thread::wait(1000);
}
return 0;
}
This is the code in box file:
#include "uvisor-lib/uvisor-lib.h"
#include "mbed.h"
#include "main-hw.h"
#include "EthernetInterface.h"
// Network interface
EthernetInterface net;
struct box_context {
Thread * thread;
uint32_t heartbeat;
};
static const UvisorBoxAclItem acl[] = {
};
static void my_box_main(const void *);
/* Box configuration
* We need 1kB of stack both in the main and interrupt threads as both of them
* use printf. */
UVISOR_BOX_NAMESPACE(NULL);
UVISOR_BOX_HEAPSIZE(3072);
UVISOR_BOX_MAIN(my_box_main, osPriorityNormal, 1024);
UVISOR_BOX_CONFIG(my_box, acl, 1024, box_context);
static void my_box_main(const void *)
{
while (1) {
printf("tan tan\r\n");
Thread::wait(2000);
}
}
I have not yet added the specific connection code, just the definition of the EthernetInterface object and I am getting the following error on compilation:
../../../../arm-none-eabi/bin/ld.exe: Region m_data_2 overflowed with stack and heap
collect2.exe: error: ld returned 1 exit status
I have tried changing the values of the heap size but I have not found a way of making it work. What am I missing?
In your main box, change the value for UVISOR_SET_PAGE_HEAP.
With UVISOR_SET_PAGE_HEAP(8 * 1024, 3) in the main box; and 8K heap in the secure box and UVISOR_BOX_STACK_SIZE stack size in the secure box it compiles and links for me (mbed OS 5.3, GCC ARM on K64F).

C and xlib: Crash in XPending() in a very simple program

I'm a beginner with xlib. I tried to program an Xwindow application using select() based on this example: how to quit the blocking of xlib's XNextEvent.
But the code mentioned above crashes in the XPending function. So I tried to isolate the problem and on my system even this from my point of view very simple example crashes on line 28. Due to several tests I assume is crashes internally by calling XFlush(). At the line marked with "// <- CRASH" I think XPending should return 0. Is that correct?
#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
Display *dis;
Window win;
int x11_fd;
fd_set in_fds;
struct timeval tv;
XKeyEvent ev;
int main() {
dis = XOpenDisplay(NULL);
win = XCreateSimpleWindow(dis, RootWindow(dis, 0), 1, 1, 256, 256, \
0, BlackPixel (dis, 0), BlackPixel(dis, 0));
XSelectInput(dis, win,
KeyPressMask | KeyReleaseMask );
XMapWindow(dis, win);
XFlush(dis);
printf("Xpending: %d\n",XPending(dis));
printf("Xpending: %d\n",XPending(dis));
XPeekEvent(dis, (XEvent*) &ev);
printf("type: %d button: 0x%X state: 0x%x\n",ev.type, ev.keycode, ev.state);
int j = XPending(dis); // <- CRASH
printf("XPending: %d\n",j);
return 0;
}
Is this a bug or have I mistaken a major concept in xlib?
XKeyEvent is bigger in bytes than XEvent
when you receive a bigger event, the ev variable will overflow.
using malloc, you actualy get more bytes than you asked, solving the problem.
better solution will be using a XEvent ev;
XEvent ev;
...
XNextEvent( & ev );
...
printf( "%d\n", ev.xkey.keycode
);
I have solved the issue. Although I don't understand why it works:
Instead of using a global ev variable I declared it inside the main function body:
XKeyEvent *ev = (XKeyEvent*) malloc(sizeof(XKeyEvent));
and adapted its pointer nature to the program.

wsdisplay color map not being retrieved correctly

I've been trying to use wscons and wsdisplay on NetBSD 5.1.2 (using the VESA framebuffer implementation) recently and I've encountered a bit of a problem:
I can set color maps successfully and they look correct but getting color maps seems to return incorrect data, such that when I try to restore the original color map once the program has finished, all the colors are incorrect:
Here's a reduced program causing the problem (note that it must be run either as root or as the user logged in on the second virtual terminal (/dev/ttyE1)):
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <dev/wscons/wsconsio.h>
int main(int argc, char **argv) {
(void)argc, (void)argv;
int tty = open("/dev/ttyE1", O_RDWR | O_EXCL);
if(tty == -1) {
perror("error opening tty");
return EXIT_FAILURE;
}
struct wsdisplay_fbinfo fbinfo;
if(ioctl(tty, WSDISPLAYIO_GINFO, &fbinfo) == -1) {
perror("error retrieving framebuffer info");
close(tty);
return EXIT_FAILURE;
}
uint8_t *cmap_data = malloc(fbinfo.cmsize * 3);
if(cmap_data == NULL) {
perror("error allocating memory for color map data");
close(tty);
return EXIT_FAILURE;
}
struct wsdisplay_cmap cmap;
cmap.index = 0;
cmap.count = fbinfo.cmsize;
cmap.red = &cmap_data[fbinfo.cmsize * 0];
cmap.green = &cmap_data[fbinfo.cmsize * 1];
cmap.blue = &cmap_data[fbinfo.cmsize * 2];
if(ioctl(tty, WSDISPLAYIO_GETCMAP, &cmap) == -1) {
perror("error getting color map");
close(tty), free(cmap_data);
return EXIT_FAILURE;
}
if(ioctl(tty, WSDISPLAYIO_PUTCMAP, &cmap) == -1) {
perror("error putting color map");
close(tty), free(cmap_data);
return EXIT_FAILURE;
}
free(cmap_data);
close(tty);
return EXIT_SUCCESS;
}
What am I doing wrong and how can I make it retrieve and restore color maps correctly?
Cause
I looked into the problem more and it appears that some kernel memory is either uninitialized or getting corrupted. Specifically, sc_cmap_red, sc_cmap_green, and sc_cmap_blue of struct vesafb_softc (in vesafbvar.h on lines 89 to 91) contain incorrect data. This is somewhat surprising, as lines 719 to 722 of vesafb.c initialize it:
/* Fill in the softc colourmap arrays */
sc->sc_cmap_red[i / 3] = rasops_cmap[i + 0];
sc->sc_cmap_green[i / 3] = rasops_cmap[i + 1];
sc->sc_cmap_blue[i / 3] = rasops_cmap[i + 2];
It contains incorrect data even if I move that out of the if statement it's in, so it might be getting corrupted rather than being uninitialized.
The driver is able to get and set color maps correctly, however; it just cannot seem to get the initial one in struct vesafb_softc right.
Workaround
A simple solution would be to make a program re-set the default color map. As the above snippet indicated, it was supposed to get its initial colors from rasops_cmap, which happens to be defined on lines 55 to 122 in rasops.c:
/* ANSI colormap (R,G,B). Upper 8 are high-intensity */
const u_char rasops_cmap[256*3] = {
/* ... */
};
With those colors, you can make a program that sets that as the current color map. I had to make a few changes so the cursor didn't disappear, but it mostly worked.
Better Solution
As I was looking around for more information, I had found this blog post. When I recompiled the kernel with genfb(4) rather than vesafb(4), the kernel hung at boot. It turns out this was because the bootloader I was using was not new enough to pass the required parameters to the kernel.
I happened to look at the NetBSD 6.0 changelog and noticed this entry:
amd64, i386
The bootloader has been enhanced to support framebuffer consoles using VESA BIOS extensions. These changes allow the x86 ports to work with the genfb(4) driver, and obsoletes the i386-only vesafb(4) driver. [jmcneill 20090216]
I downloaded NetBSD 6.0_BETA and booted it from the boot prompt like this:
> vesa 640x480x8
> boot netbsd
...and everything worked.
In short, using a newer version of NetBSD and ditching vesafb(4) solves the problem.

Resources