android emulator虚拟设备分析第四篇之framebuffer

简介: 一、概述 framebuffer是啥就不用详细说了吧,需要注意的是android emulator的framebuffer貌似用处不大,因为我之前用android emulator运行x86镜像时,可能是分辨率选得太大了,/dev/graphics/fb0文件都没有产生,系统却可以正常跑,因为系统用的是DRM方式。

一、概述

framebuffer是啥就不用详细说了吧,需要注意的是android emulator的framebuffer貌似用处不大,因为我之前用android emulator运行x86镜像时,可能是分辨率选得太大了,/dev/graphics/fb0文件都没有产生,系统却可以正常跑,因为系统用的是DRM方式。

另外,GOLDFISH-VIRTUAL-HARDWARE.TXT说了

IMPORTANT NOTE: When GPU emulation is enabled, the framebuffer will typically only be used during boot.

所以,本篇可以跳过不看。


二、驱动

老规矩,先看文档

V. Goldfish framebuffer:
========================

Relevant files:
  $QEMU/hw/android/goldfish/fb.c
  $KERNEL/drivers/video/goldfish_fb.c

Device properties:
  Name: goldfish_fb
  Id: 0 to N  (only one used in practice).
  IrqCount: 0
  I/O Registers:
    0x00  GET_WIDTH       R: Read framebuffer width in pixels.
    0x04  GET_HEIGHT      R: Read framebuffer height in pixels.
    0x08  INT_STATUS
    0x0c  INT_ENABLE
    0x10  SET_BASE
    0x14  SET_ROTATION
    0x18  SET_BLANK       W: Set 'blank' flag.
    0x1c  GET_PHYS_WIDTH  R: Read framebuffer width in millimeters.
    0x20  GET_PHYS_HEIGHT R: Read framebuffer height in millimeters.
    0x24  GET_FORMAT      R: Read framebuffer pixel format.

The framebuffer device is a bit peculiar, because it uses, in addition to the
typical I/O registers and IRQs, a large area of physical memory, allocated by
the kernel, but visible to the emulator, to store a large pixel buffer.

The emulator is responsible for displaying the framebuffer content in its UI
window, which can be rotated, as instructed by the kernel.

IMPORTANT NOTE: When GPU emulation is enabled, the framebuffer will typically
only be used during boot. Note that GPU emulation doesn't rely on a specific
virtual GPU device, however, it uses the "QEMU Pipe" device described below.
For more information, please read:

  https://android.googlesource.com/platform/sdk/+/master/emulator/opengl/DESIGN

On boot, the kernel will read various properties of the framebuffer:

  IO_READ(GET_WIDTH) and IO_READ(GET_HEIGHT) return the width and height of
  the framebuffer in pixels. Note that a 'row' corresponds to consecutive bytes
  in memory, but doesn't necessarily to an horizontal line on the final display,
  due to possible rotation (see SET_ROTATION below).

  IO_READ(GET_PHYS_WIDTH) and IO_READ(GET_PHYS_HEIGHT) return the emulated
  physical width and height in millimeters, this is later used by the kernel
  and the platform to determine the device's emulated density.

  IO_READ(GET_FORMAT) returns a value matching the format of pixels in the
  framebuffer. Note that these values are specified by the Android hardware
  abstraction layer (HAL) and cannot change:

    0x01  HAL_PIXEL_FORMAT_BRGA_8888
    0x02  HAL_PIXEL_FORMAT_RGBX_8888
    0x03  HAL_PIXEL_FORMAT_RGB_888
    0x04  HAL_PIXEL_FORMAT_RGB_565
    0x05  HAL_PIXEL_FORMAT_BGRA_8888
    0x06  HAL_PIXEL_FORMAT_RGBA_5551
    0x08  HAL_PIXEL_FORMAT_RGBA_4444

  HOWEVER, the kernel driver only expects a value of HAL_PIXEL_FORMAT_RGB_565
  at the moment. Until this is fixed, the virtual device should always return
  the value 0x04 here. Rows are not padded, so the size in bytes of a single
  framebuffer will always be exactly 'width * heigth * 2'.

  Note that GPU emulation doesn't have this limitation and can use and display
  32-bit surfaces properly, because it doesn't use the framebuffer.

The device has a 'blank' flag. When set to 1, the UI should only display an
empty/blank framebuffer, ignoring the content of the framebuffer memory.
It is set with IO_WRITE(SET_BLANK, <value>), where value can be 1 or 0. This is
used when simulating suspend/resume.

IMPORTANT: The framebuffer memory is allocated by the kernel, which will send
its physical address to the device by using IO_WRITE(SET_BASE, <address>).

The kernel really allocates a memory buffer large enough to hold *two*
framebuffers, in order to implement panning / double-buffering. This also means
that calls to IO_WRITE(SET_BASE, <address>) will be frequent.

The allocation happens with dma_alloc_writecombine() on ARM, which can only
allocate a maximum of 4 MB, this limits the size of each framebuffer to 2 MB,
which may not be enough to emulate high-density devices :-(

For other architectures, dma_alloc_coherent() is used instead, and has the same
upper limit / limitation.

TODO(digit): Explain how it's possible to raise this limit by modifyinf
             CONSISTENT_DMA_SIZE and/or MAX_ORDER in the kernel configuration.

The device uses a single IRQ to notify the kernel of several events. When it
is raised, the kernel IRQ handler must IO_READ(INT_STATUS), which will return
a value containing the following bit flags:

  bit 0: Set to 1 to indicate a VSYNC event.

  bit 1: Set to 1 to indicate that the content of a previous SET_BASE has
         been properly displayed.

Note that reading this register also lowers the device's IRQ level.

The second flag is essentially a way to notify the kernel that an
IO_WRITE(SET_BASE, <address>) operation has been succesfully processed by
the emulator, i.e. that the new content has been displayed to the user.

The kernel can control which flags should raise an IRQ by using
IO_WRITE(INT_ENABLE, <flags>), where <flags> has the same format as the
result of IO_READ(INT_STATUS). If the corresponding bit is 0, the an IRQ
for the corresponding event will never be generated,
注意,framebuffer的驱动只支持HAL_PIXEL_FORMAT_RGB_565这一种格式,GPU的不受这个限制。另外,驱动程序中VSYNC的中断其实没有使用。

驱动程序为goldfish中的drivers/video/goldfishfb.c

初始化:

static struct platform_driver goldfish_fb_driver = {
    .probe      = goldfish_fb_probe,
    .remove     = goldfish_fb_remove,
    .driver = {
        .name = "goldfish_fb"
    }
};

static int __init goldfish_fb_init(void)
{
    return platform_driver_register(&goldfish_fb_driver);
}

static void __exit goldfish_fb_exit(void)
{
    platform_driver_unregister(&goldfish_fb_driver);
}

module_init(goldfish_fb_init);
module_exit(goldfish_fb_exit);



goldfish_fb_probe,初始化了一下fb中的spin_lock和waitqueue,然后还是获得IO内存,ioremap,获得中断号,设置中断函数,老套路了。

设置fb的一些属性,比如像素,物理尺寸,图像格式等。

注意申请framebuffer使用的内存时,goldfish_fb_memblock_map总是失败的,因为goldfish platform bus上的设备, IORESOURCE_MEM只有一个,fbmem = platform_get_resource(pdev, IORESOURCE_MEM, 1)肯定会失败的,使用-show-kernel可以看到错误提示"no framebuffer memblock"。

实际使用的是goldfish_fb_dma_alloc,使用dma分配的物理内存,大小为两屏,使用FB_SET_BASE时将其中一屏的地址传递给虚拟设备(双缓存时,这种操作会执行很多次),虚拟设备去把里面的东西画出来。

static int __devinit goldfish_fb_probe(struct platform_device *pdev)
{
    int ret;
    struct resource *r;
    struct goldfish_fb *fb;
    size_t framesize;
    uint32_t width, height;

    fb = kzalloc(sizeof(*fb), GFP_KERNEL);
    if(fb == NULL) {
        ret = -ENOMEM;
        goto err_fb_alloc_failed;
    }
    spin_lock_init(&fb->lock);
    init_waitqueue_head(&fb->wait);
    platform_set_drvdata(pdev, fb);

    r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if(r == NULL) {
        ret = -ENODEV;
        goto err_no_io_base;
    }
#if defined(CONFIG_ARM)
    fb->reg_base = (void __iomem *)IO_ADDRESS(r->start - IO_START);
#elif defined(CONFIG_X86) || defined(CONFIG_MIPS)
    fb->reg_base = ioremap(r->start, PAGE_SIZE);
#else
#error NOT SUPPORTED
#endif

    fb->irq = platform_get_irq(pdev, 0);
    if(fb->irq < 0) {
        ret = -ENODEV;
        goto err_no_irq;
    }

    width = readl(fb->reg_base + FB_GET_WIDTH);
    height = readl(fb->reg_base + FB_GET_HEIGHT);

    fb->fb.fbops        = &goldfish_fb_ops;
    fb->fb.flags        = FBINFO_FLAG_DEFAULT;
    fb->fb.pseudo_palette   = fb->cmap;
    //strncpy(fb->fb.fix.id, clcd_name, sizeof(fb->fb.fix.id));

    fb->fb.fix.type     = FB_TYPE_PACKED_PIXELS;
    fb->fb.fix.visual = FB_VISUAL_TRUECOLOR;
    fb->fb.fix.line_length = width * 2;
    fb->fb.fix.accel    = FB_ACCEL_NONE;
    fb->fb.fix.ypanstep = 1;

    fb->fb.var.xres     = width;
    fb->fb.var.yres     = height;
    fb->fb.var.xres_virtual = width;
    fb->fb.var.yres_virtual = height * 2;
    fb->fb.var.bits_per_pixel = 16;
    fb->fb.var.activate = FB_ACTIVATE_NOW;
    fb->fb.var.height   = readl(fb->reg_base + FB_GET_PHYS_HEIGHT);
    fb->fb.var.width    = readl(fb->reg_base + FB_GET_PHYS_WIDTH);
    fb->fb.var.pixclock = 10000;

    fb->fb.var.red.offset = 11;
    fb->fb.var.red.length = 5;
    fb->fb.var.green.offset = 5;
    fb->fb.var.green.length = 6;
    fb->fb.var.blue.offset = 0;
    fb->fb.var.blue.length = 5;

    framesize = width * height * 2 * 2;
    ret = goldfish_fb_memblock_map(fb, pdev, framesize);
    if (ret < 0)
        ret = goldfish_fb_dma_alloc(fb, pdev, width, height, framesize);
    if (ret < 0)
        goto err_alloc_screen_base_failed;

    ret = fb_set_var(&fb->fb, &fb->fb.var);
    if(ret)
        goto err_fb_set_var_failed;

    ret = request_irq(fb->irq, goldfish_fb_interrupt, IRQF_SHARED, pdev->name, fb);
    if(ret)
        goto err_request_irq_failed;

    writel(FB_INT_BASE_UPDATE_DONE, fb->reg_base + FB_INT_ENABLE);
    goldfish_fb_pan_display(&fb->fb.var, &fb->fb); // updates base


    ret = register_framebuffer(&fb->fb);
    if(ret)
        goto err_register_framebuffer_failed;

#ifdef CONFIG_ANDROID_POWER
    fb->early_suspend.suspend = goldfish_fb_early_suspend;
    fb->early_suspend.resume = goldfish_fb_late_resume;
    android_register_early_suspend(&fb->early_suspend);
#endif

    return 0;


err_register_framebuffer_failed:
    free_irq(fb->irq, fb);
err_request_irq_failed:
err_fb_set_var_failed:
    goldfish_fb_mem_free(fb, pdev);
err_alloc_screen_base_failed:
err_no_irq:
    goldfish_fb_regs_free(fb);
err_no_io_base:
    kfree(fb);
err_fb_alloc_failed:
    return ret;
}


goldfish_fb_interrupt中断函数,如果中断标志位FB_INT_BASE_UPDATE_DONE有效,说明虚拟设备已得知base改变,准备重画(尚未开始画),然后就去唤醒waitqueue上等待的线程。虚拟设备中对应的代码为goldfish_fb_update_display函数。

static irqreturn_t
goldfish_fb_interrupt(int irq, void *dev_id)
{
 unsigned long irq_flags;
 struct goldfish_fb  *fb = dev_id;
 uint32_t status;

 spin_lock_irqsave(&fb->lock, irq_flags);
 status = readl(fb->reg_base + FB_INT_STATUS);
 if(status & FB_INT_BASE_UPDATE_DONE) {
     fb->base_update_count++;
     wake_up(&fb->wait);
 }
 spin_unlock_irqrestore(&fb->lock, irq_flags);
 return status ? IRQ_HANDLED : IRQ_NONE;
}



waitqueue上等待的线程从哪里来的呢?goldfish_fb_pan_display函数干的。使用FB_SET_BASE命令改变base,然后设备去处理,但不用等画完。

static int goldfish_fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
{
    unsigned long irq_flags;
    int base_update_count;
    struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb);

    spin_lock_irqsave(&fb->lock, irq_flags);
    base_update_count = fb->base_update_count;
    writel(fb->fb.fix.smem_start + fb->fb.var.xres * 2 * var->yoffset, fb->reg_base + FB_SET_BASE);
    spin_unlock_irqrestore(&fb->lock, irq_flags);
    wait_event_timeout(fb->wait, fb->base_update_count != base_update_count, HZ / 15);
    if(fb->base_update_count == base_update_count)
        printk("goldfish_fb_pan_display: timeout wating for base update\n");
    return 0;
}


goldfish_fb_set_par设置一些参数的,比如旋转

static int goldfish_fb_set_par(struct fb_info *info)
{
    struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb);
    if(fb->rotation != fb->fb.var.rotate) {
        info->fix.line_length = info->var.xres * 2;
        fb->rotation = fb->fb.var.rotate;
        writel(fb->rotation, fb->reg_base + FB_SET_ROTATION);
    }
    return 0;
}


goldfish_fb_set_par和goldfish_fb_pan_display都是在fbmem.c的do_fb_ioctl中调用的。


goldfish_fb_early_suspend用于在suspend时,画一个纯黑的图

static void goldfish_fb_early_suspend(android_early_suspend_t *h)
{
    struct goldfish_fb *fb = container_of(h, struct goldfish_fb, early_suspend);
    writel(1, fb->reg_base + FB_SET_BLANK);
}


goldfish_fb_late_resume和goldfish_fb_early_suspend对应的

static void goldfish_fb_late_resume(android_early_suspend_t *h)
{
    struct goldfish_fb *fb = container_of(h, struct goldfish_fb, early_suspend);
        writel(0, fb->reg_base + FB_SET_BLANK);
}

三、虚拟设备

虚拟设备的代码为hw/android/goldfish/fb.c


goldfish_fb_init初始化,s->ds = graphic_console_init返回的格式是32bit的,和驱动中固定支持的565不同,不过驱动压根就没有用GET_FORMAT去看虚拟设备支持的格式,所以就不管它了。另外,dpi被写死了s->dpi = 165

void goldfish_fb_init(int id)
{
    struct goldfish_fb_state *s;

    s = (struct goldfish_fb_state *)g_malloc0(sizeof(*s));
    s->dev.name = "goldfish_fb";
    s->dev.id = id;
    s->dev.size = 0x1000;
    s->dev.irq_count = 1;

    s->ds = graphic_console_init(goldfish_fb_update_display,
                                 goldfish_fb_invalidate_display,
                                 NULL,
                                 NULL,
                                 s);

    s->dpi = 165;  /* XXX: Find better way to get actual value ! */

    /* IMPORTANT: DO NOT COMPUTE s->pixel_format and s->bytes_per_pixel
     * here because the display surface is going to change later.
     */
    s->bytes_per_pixel = 0;
    s->pixel_format    = -1;

    goldfish_device_add(&s->dev, goldfish_fb_readfn, goldfish_fb_writefn, s);

    register_savevm(NULL,
                    "goldfish_fb",
                    0,
                    GOLDFISH_FB_SAVE_VERSION,
                    goldfish_fb_save,
                    goldfish_fb_load,
                    s);
}


读写函数也比较简单,注意读函数中的FB_GET_FORMAT没有被驱动程序使用,写函数FB_SET_BASE后会设置need_update=1, need_int=1之类的状态

static uint32_t goldfish_fb_read(void *opaque, hwaddr offset)
{
    uint32_t ret;
    struct goldfish_fb_state *s = opaque;

    switch(offset) {
        case FB_GET_WIDTH:
            ret = ds_get_width(s->ds);
            //printf("FB_GET_WIDTH => %d\n", ret);

            return ret;

        case FB_GET_HEIGHT:
            ret = ds_get_height(s->ds);
            //printf( "FB_GET_HEIGHT = %d\n", ret);

            return ret;

        case FB_INT_STATUS:
            ret = s->int_status & s->int_enable;
            if(ret) {
                s->int_status &= ~ret;
                goldfish_device_set_irq(&s->dev, 0, 0);
            }
            return ret;

        case FB_GET_PHYS_WIDTH:
            ret = pixels_to_mm( ds_get_width(s->ds), s->dpi );
            //printf( "FB_GET_PHYS_WIDTH => %d\n", ret );

            return ret;

        case FB_GET_PHYS_HEIGHT:
            ret = pixels_to_mm( ds_get_height(s->ds), s->dpi );
            //printf( "FB_GET_PHYS_HEIGHT => %d\n", ret );

            return ret;

        case FB_GET_FORMAT:
            return goldfish_fb_get_pixel_format(s);

        default:
            cpu_abort(cpu_single_env,
                      "goldfish_fb_read: Bad offset %" HWADDR_PRIx "\n",
                      offset);
            return 0;
    }
}

static void goldfish_fb_write(void *opaque, hwaddr offset,
                        uint32_t val)
{
    struct goldfish_fb_state *s = opaque;

    switch(offset) {
        case FB_INT_ENABLE:
            s->int_enable = val;
            goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
            break;
        case FB_SET_BASE: {
            int need_resize = !s->base_valid;
            s->fb_base = val;
            s->int_status &= ~FB_INT_BASE_UPDATE_DONE;
            s->need_update = 1;
            s->need_int = 1;
            s->base_valid = 1;
            if(s->set_rotation != s->rotation) {
                //printf("FB_SET_BASE: rotation : %d => %d\n", s->rotation, s->set_rotation);

                s->rotation = s->set_rotation;
                need_resize = 1;
            }
            goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
            if (need_resize) {
                //printf("FB_SET_BASE: need resize (rotation=%d)\n", s->rotation );

                dpy_resize(s->ds);
            }
            } break;
        case FB_SET_ROTATION:
            //printf( "FB_SET_ROTATION %d\n", val);

            s->set_rotation = val;
            break;
        case FB_SET_BLANK:
            s->blank = val;
            s->need_update = 1;
            break;
        default:
            cpu_abort(cpu_single_env,
                      "goldfish_fb_write: Bad offset %" HWADDR_PRIx "\n",
                      offset);
    }
}


ANDROID-FRAMEBUFFER.TXT中:

The pixel buffer is itself a set of physical pages allocated by the
kernel driver in the emulated system. These pages are contiguous in
the emulated system, but not in the emulator's process space which
places them randomly in the heap.

Also, a function called goldfish_fb_update_display() is in charge of
checking the dirty bits of the framebuffer physical pages, in order to
compute the bounding rectangle of pixel updates since the last call, and
send them to the UI through qframebuffer_update(). More on this later.



goldfish_fb_update_display会被循环调用,如果base变化了,会重画全屏(常用的双缓存方式)。否则调用compute_fb_update_rect_linear判断需要重画的区域并重画(直接打开/dev/fb0开画就是这样的)。

static void goldfish_fb_update_display(void *opaque)
{
    struct goldfish_fb_state *s = (struct goldfish_fb_state *)opaque;
    uint32_t base;
    uint8_t*  dst_line;
    uint8_t*  src_line;
    int full_update = 0;
    int  width, height, pitch;

    base = s->fb_base;
    if(base == 0)
        return;

    if((s->int_enable & FB_INT_VSYNC) && !(s->int_status & FB_INT_VSYNC)) {
        s->int_status |= FB_INT_VSYNC;
        goldfish_device_set_irq(&s->dev, 0, 1);
    }

    if(s->need_update) {
        full_update = 1;
        if(s->need_int) {
            s->int_status |= FB_INT_BASE_UPDATE_DONE;
            if(s->int_enable & FB_INT_BASE_UPDATE_DONE)
                goldfish_device_set_irq(&s->dev, 0, 1);
        }
        s->need_int = 0;
        s->need_update = 0;
    }

    src_line  = qemu_get_ram_ptr( base );

    dst_line  = s->ds->surface->data;
    pitch     = s->ds->surface->linesize;
    width     = s->ds->surface->width;
    height    = s->ds->surface->height;

    FbUpdateState  fbs;
    FbUpdateRect   rect;

    fbs.width      = width;
    fbs.height     = height;
    fbs.dst_pixels = dst_line;
    fbs.dst_pitch  = pitch;
    fbs.bytes_per_pixel = goldfish_fb_get_bytes_per_pixel(s);

    fbs.src_pixels = src_line;
    fbs.src_pitch  = width*s->ds->surface->pf.bytes_per_pixel;


#if STATS
    if (full_update)
        stats_full_updates += 1;
    if (++stats_counter == 120) {
        stats_total               += stats_counter;
        stats_total_full_updates  += stats_full_updates;

        printf( "full update stats:  peak %.2f %%  total %.2f %%\n",
                stats_full_updates*100.0/stats_counter,
                stats_total_full_updates*100.0/stats_total );

        stats_counter      = 0;
        stats_full_updates = 0;
    }
#endif /* STATS */

    if (s->blank)
    {
        memset( dst_line, 0, height*pitch );
        rect.xmin = 0;
        rect.ymin = 0;
        rect.xmax = width-1;
        rect.ymax = height-1;
    }
    else
    {
        if (full_update) { /* don't use dirty-bits optimization */
            base = 0;
        }
        if (compute_fb_update_rect_linear(&fbs, base, &rect) == 0) {
            return;
        }
    }

    rect.xmax += 1;
    rect.ymax += 1;
#if 0
    printf("goldfish_fb_update_display (y:%d,h:%d,x=%d,w=%d)\n",
           rect.ymin, rect.ymax-rect.ymin, rect.xmin, rect.xmax-rect.xmin);
#endif

    dpy_update(s->ds, rect.xmin, rect.ymin, rect.xmax-rect.xmin, rect.ymax-rect.ymin);
}


goldfish_fb_get_pixel_format,由于驱动没有使用FB_GET_FORMAT寄存器,所以这个函数没啥用处


四、测试程序

来自http://www.linuxquestions.org/questions/programming-9/hello-world-or-display-pixels-on-framebuffer-fb-h-4175539854/上的一个例子

#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");

    // 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;

    // Map the device to memory
    fbp = (char *)mmap(0, screensize, 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");

    // Figure out where in memory to put the pixel
    for (y = 100; y < 300; y++)
        for (x = 100; x < 300; x++) {

            location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/8) +
                       (y+vinfo.yoffset) * finfo.line_length;

            if (vinfo.bits_per_pixel == 32) {
                *(fbp + location) = 100;        // Some blue
                *(fbp + location + 1) = 15+(x-100)/2;     // A little green
                *(fbp + location + 2) = 200-(y-100)/5;    // A lot of red
                *(fbp + location + 3) = 0;      // No transparency
            } else  { //assume 16bpp
                int b = 10;
                int g = (x-100)/6;     // A little green
                int r = 31-(y-100)/16;    // A lot of red
                unsigned short int t = r<<11 | g << 5 | b;
                *((unsigned short int*)(fbp + location)) = t;
            }

        }
    munmap(fbp, screensize);
    close(fbfd);
    return 0;
}

在android emulator上运行,没有反应。。。

找一个linux机器,带有framebuffer的,测试OK

难怪前面说android的framebuffer没有什么用处。。。


还有一个狂野点的测试程序dd

sudo dd if=/dev/fb0 of=fbdata.bin bs=1M count=2

随便移动窗口或者其他方式改变一下桌面

sudo dd if=fbdata.bin of=/dev/fb0 bs=1M count=2

看到桌面恢复了,然后鼠标滑过的地方会被更新


PS:生命诚可贵,不要用dd。以前搞路由器时,就这样把电脑的sda给dd没了的。


相关实践学习
部署Stable Diffusion玩转AI绘画(GPU云服务器)
本实验通过在ECS上从零开始部署Stable Diffusion来进行AI绘画创作,开启AIGC盲盒。
目录
相关文章
|
26天前
|
开发工具 Android开发 Swift
安卓与iOS开发环境对比分析
在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统无疑是主角。它们各自拥有独特的特点和优势,为开发者提供了不同的开发环境和工具。本文将深入浅出地探讨安卓和iOS开发环境的主要差异,包括开发工具、编程语言、用户界面设计、性能优化以及市场覆盖等方面,旨在帮助初学者更好地理解两大平台的开发特点,并为他们选择合适的开发路径提供参考。通过比较分析,我们将揭示不同环境下的开发实践,以及如何根据项目需求和目标受众来选择最合适的开发平台。
34 2
|
2月前
|
Shell Linux 开发工具
"开发者的救星:揭秘如何用adb神器征服Android设备,开启高效调试之旅!"
【8月更文挑战第20天】Android Debug Bridge (adb) 是 Android 开发者必备工具,用于实现计算机与 Android 设备间通讯,执行调试及命令操作。adb 提供了丰富的命令行接口,覆盖从基础设备管理到复杂系统操作的需求。本文详细介绍 adb 的安装配置流程,并列举实用命令示例,包括设备连接管理、应用安装调试、文件系统访问等基础功能,以及端口转发、日志查看等高级技巧。此外,还提供了常见问题的故障排除指南,帮助开发者快速解决问题。掌握 adb 将极大提升 Android 开发效率,助力项目顺利推进。
52 0
|
7天前
|
安全 Android开发 数据安全/隐私保护
探索安卓与iOS的安全性差异:技术深度分析与实践建议
本文旨在深入探讨并比较Android和iOS两大移动操作系统在安全性方面的不同之处。通过详细的技术分析,揭示两者在架构设计、权限管理、应用生态及更新机制等方面的安全特性。同时,针对这些差异提出针对性的实践建议,旨在为开发者和用户提供增强移动设备安全性的参考。
|
14天前
|
安全 Linux Android开发
探索安卓与iOS的安全性差异:技术深度分析
本文深入探讨了安卓(Android)和iOS两个主流操作系统平台在安全性方面的不同之处。通过比较它们在架构设计、系统更新机制、应用程序生态和隐私保护策略等方面的差异,揭示了每个平台独特的安全优势及潜在风险。此外,文章还讨论了用户在使用这些设备时可以采取的一些最佳实践,以增强个人数据的安全。
|
1月前
|
IDE 开发工具 Android开发
安卓与iOS开发环境对比分析
本文将探讨安卓和iOS这两大移动操作系统在开发环境上的差异,从工具、语言、框架到生态系统等多个角度进行比较。我们将深入了解各自的优势和劣势,并尝试为开发者提供一些实用的建议,以帮助他们根据自己的需求选择最适合的开发平台。
28 1
|
2月前
|
Android开发
基于Amlogic 安卓9.0, 驱动简说(四):Platform平台驱动,驱动与设备的分离
本文介绍了如何在基于Amlogic T972的Android 9.0系统上使用Platform平台驱动框架和设备树(DTS),实现设备与驱动的分离,并通过静态枚举在设备树中描述设备,自动触发驱动程序的加载和设备创建。
18 0
基于Amlogic 安卓9.0, 驱动简说(四):Platform平台驱动,驱动与设备的分离
|
2月前
|
Android开发 C语言
基于Amlogic 安卓9.0, 驱动简说(二):字符设备驱动,自动创建设备
这篇文章是关于如何在基于Amlogic T972的Android 9.0系统上,通过自动分配设备号和自动创建设备节点文件的方式,开发字符设备驱动程序的教程。
36 0
基于Amlogic 安卓9.0, 驱动简说(二):字符设备驱动,自动创建设备
|
2月前
|
自然语言处理 Shell Linux
基于Amlogic 安卓9.0, 驱动简说(一):字符设备驱动,手动创建设备
本文是关于在Amlogic安卓9.0平台上创建字符设备驱动的教程,详细介绍了驱动程序的编写、编译、部署和测试过程,并提供了完整的源码和应用层调用示例。
50 0
基于Amlogic 安卓9.0, 驱动简说(一):字符设备驱动,手动创建设备
|
2月前
|
传感器 Android开发 芯片
不写一行代码(三):实现安卓基于i2c bus的Slaver设备驱动
本文是系列文章的第三篇,展示了如何在Android系统中利用现有的i2c bus驱动,通过编写设备树节点和应用层的控制代码,实现对基于i2c bus的Slaver设备(如六轴陀螺仪模块QMI8658C)的控制,而无需编写设备驱动代码。
30 0
不写一行代码(三):实现安卓基于i2c bus的Slaver设备驱动
|
2月前
|
Android开发
不写一行代码(二):实现安卓基于PWM的LED设备驱动
本文介绍了在Android系统中不编写任何代码,通过设备树配置和内核支持的通用PWM LED驱动来实现基于PWM的LED设备驱动,并通过测试命令调整LED亮度级别。
34 0
不写一行代码(二):实现安卓基于PWM的LED设备驱动
下一篇
无影云桌面