4

I have an embedded system and want to use /dev/fb0 directly. As a first test, I use some code based on example-code found everywhere in the net and SO. Opening succeeds, also fstat and similar. But mmap fails with EINVAL.

Source:

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <sys/ioctl.h>

int main() {
    int fbfd = 0;

    struct fb_var_screeninfo vinfo;
    struct fb_fix_screeninfo finfo;
    long int screensize = 0;
    char *fbp = 0;
    int x = 0, y = 0;
    long int location = 0;

    // Open the file for reading and writing
    fbfd = open("/dev/fb0", O_RDWR);
    if (fbfd == -1) {
        perror("Error: cannot open framebuffer device");
        exit(1);
    }
    printf("The framebuffer device was opened successfully.\n");

    struct stat stat;
    fstat(fbfd, &stat);
    printf("/dev/mem -> size: %u blksize: %u blkcnt: %u\n", 
            stat.st_size, stat.st_blksize, stat.st_blocks);

    // Get fixed screen information
    if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo) == -1) {
        perror("Error reading fixed information");
        exit(2);
    }

    // Get variable screen information
    if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo) == -1) {
        perror("Error reading variable information");
        exit(3);
    }

    printf("%dx%d, %dbpp\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);

    // Figure out the size of the screen in bytes
    screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;
    const int PADDING = 4096;
    int mmapsize = (screensize + PADDING - 1) & ~(PADDING-1);

    // Map the device to memory
    fbp = (char *)mmap(0, mmapsize, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0);
    if ((int)fbp == -1) {
        perror("Error: failed to map framebuffer device to memory");
        exit(4);
    }
    printf("The framebuffer device was mapped to memory successfully.\n");

    munmap(fbp, screensize);
    close(fbfd);

    return 0;
}

Output:

The framebuffer device was opened successfully.
/dev/mem -> size: 0 blksize: 4096 blkcnt: 0
640x480, 4bpp
Error: failed to map framebuffer device to memory: Invalid argument

strace:

...
open("/dev/fb0", O_RDWR)                = 3
write(1, "The framebuffer device was opene"..., 48The framebuffer device was opened successfully.
) = 48
fstat64(3, {st_mode=S_IFCHR|0640, st_rdev=makedev(29, 0), ...}) = 0
write(1, "/dev/mem -> size: 0 blksize: 409"..., 44/dev/mem -> size: 0 blksize: 4096 blkcnt: 0
) = 44
ioctl(3, FBIOGET_FSCREENINFO or FBIOPUT_CONTRAST, 0xbfca6564) = 0
ioctl(3, FBIOGET_VSCREENINFO, 0xbfca6600) = 0
write(1, "640x480, 4bpp\n", 14640x480, 4bpp
)         = 14
old_mmap(NULL, 155648, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0) = -1 EINVAL (Invalid argument)
write(2, "Error: failed to map framebuffer"..., 49Error: failed to map framebuffer device to memory) = 49
write(2, ": ", 2: )                       = 2
write(2, "Invalid argument", 16Invalid argument)        = 16
write(2, "\n", 1
)                       = 1

The boot-screen with console and tux is visible. And cat /dev/urandom > /dev/fb0 fills the screen with noise. The pagesize is 4096 on the system (`getconf PAGESIZE). So, 155648 (0x26000) is a multiple. Offset and pointer are both zero. Mapping and filemode are both RW .. what am I missing?

This is for an embedded device build with uClibc and busybox running a single application and I have to port it from an ancient kernel. There is code for linedrawing and such and no need for multiprocessing/ windowing .. please no hints to directfb ;).

Community
  • 1
  • 1
Peter Schneider
  • 1,683
  • 12
  • 31
  • Do you know for a fact that your framebuffer driver supports mapping? Can you even read() from the framebuffer progressively (simple to test with dd)? – Chris Stratton Apr 07 '14 at 14:52
  • No. Its just a vanilla kernel and "standard"-x86-hardware. How can I check? – Peter Schneider Apr 07 '14 at 14:54
  • Perhaps you could check the validity of your mmap code against an actual file instead? As for the framebuffer, you might need to look at the source of its driver. – Chris Stratton Apr 07 '14 at 14:57
  • I have tested with /dev/mem - no problem (first MB is still valid without special kernel options). I hopped I could avoid digging deep into kernel sources :/ – Peter Schneider Apr 07 '14 at 14:59
  • I am not exactly sure about your problem, I once had an error like dev/fb0 is not accessible. I was not removing my sd card properly from the computer after copying the linux image in it ( In the BOOT partition of it). The SD card should safely removed from the computer. So I can suggest that just follow all the basic steps properly. – user3217310 Apr 23 '14 at 05:23

1 Answers1

4

The kernel driver that presents the framebuffer doesn't support the legacy direct mmap() of the framebuffer device; you need to use the newer DRM and KMS interface.

Irshad
  • 3,071
  • 5
  • 30
  • 51
  • 2
    And how would I do that? – Luc Oct 21 '18 at 12:19
  • It seems that the kernel driver **does** support `mmap()`, however it refuses to map more than N bytes of it at a time (seems to differ by hardware, but I've seen the maximum be around 3MB-8MB), which makes it not very useful for direct graphics output. – Ed Halferty Oct 16 '21 at 22:23
  • Actually, I was only able to replicate the "invalid argument" error by trying to mmap more than X times Y times bpp bytes (for 1280x800,32bpp, that's 8294400 bytes). So for anyone running into this, make sure you're not trying to map more than the size of the framebuffer. – Ed Halferty Oct 16 '21 at 22:41