| /* |
| * Copyright 2007 The Android Open Source Project |
| * |
| * Fake device support. |
| */ |
| #include "Common.h" |
| |
| #include <stdlib.h> |
| #include <string.h> |
| #include <assert.h> |
| |
| #include <sys/mman.h> |
| #include <sys/ioctl.h> |
| #include <linux/fb.h> |
| |
| typedef struct FbState { |
| /* index into gWrapSim.display[] */ |
| int displayIdx; |
| |
| /* VRAM address, set by mmap() call */ |
| void* vramAddr; |
| |
| /* kernel data structures */ |
| struct fb_var_screeninfo vinfo; |
| struct fb_fix_screeninfo finfo; |
| } FbState; |
| |
| |
| /* |
| * Set up the initial values of the structs. |
| * |
| * The FbState struct is zeroed out initially, so we only need to set the |
| * fields that don't default to zero. |
| */ |
| static void configureInitialState(int displayIdx, FbState* fbState) |
| { |
| int width, height; |
| |
| assert(displayIdx >= 0 && displayIdx < gWrapSim.numDisplays); |
| |
| width = gWrapSim.display[displayIdx].width; |
| height = gWrapSim.display[displayIdx].height; |
| wsLog("Configuring FbState for display %d (%dx%x key=0x%08x)\n", |
| displayIdx, width, height, gWrapSim.display[displayIdx].shmemKey); |
| |
| /* fb_fix_screeninfo */ |
| strcpy(fbState->finfo.id, "omapfb"); |
| fbState->finfo.smem_len = (width * 2) * height * 2; |
| fbState->finfo.line_length = width * 2; |
| |
| /* fb_var_screeninfo */ |
| fbState->vinfo.xres = width; |
| fbState->vinfo.yres = height; |
| fbState->vinfo.xres_virtual = width; |
| fbState->vinfo.yres_virtual = height * 2; |
| fbState->vinfo.bits_per_pixel = 16; |
| |
| fbState->vinfo.red.offset = 11; |
| fbState->vinfo.red.length = 5; |
| fbState->vinfo.green.offset = 5; |
| fbState->vinfo.green.length = 6; |
| fbState->vinfo.blue.offset = 0; |
| fbState->vinfo.blue.length = 5; |
| |
| fbState->vinfo.width = 51; // physical dimension, used for dpi |
| fbState->vinfo.height = 76; |
| |
| fbState->vinfo.pixclock = 103092; |
| fbState->vinfo.upper_margin = 3; |
| fbState->vinfo.lower_margin = 227; |
| fbState->vinfo.left_margin = 12; |
| fbState->vinfo.right_margin = 8; |
| } |
| |
| /* |
| * Free allocated state. |
| */ |
| static void freeState(FbState* fbState) |
| { |
| free(fbState); |
| } |
| |
| /* |
| * Wait for our synthetic vsync to happen. |
| */ |
| static void waitForVsync(FbState* state) |
| { |
| /* TODO: simulate a real interval */ |
| usleep(1000000/60); |
| } |
| |
| /* |
| * Forward pixels to the simulator. |
| */ |
| static void sendPixelsToSim(FbState* state) |
| { |
| if (state->vramAddr == 0) { |
| wsLog("## not sending pixels (no addr yet)\n"); |
| return; |
| } |
| |
| //wsLog("+++ sending pixels to sim (disp=%d yoff=%d)\n", |
| // state->displayIdx, state->vinfo.yoffset); |
| |
| wsLockDisplay(state->displayIdx); |
| |
| uint8_t* dst = gWrapSim.display[state->displayIdx].addr; |
| |
| int l,t,r,b,w,h; |
| w = gWrapSim.display[state->displayIdx].width; |
| h = gWrapSim.display[state->displayIdx].height; |
| |
| #if 0 |
| /* |
| * TODO: surfaceflinger encodes the dirty region in vinfo.reserved[]. We |
| * can use that to perform a partial update. |
| */ |
| const Rect dirty(dirtyReg.bounds()); |
| l = dirty.left >=0 ? dirty.left : 0; |
| t = dirty.top >=0 ? dirty.top : 0; |
| r = dirty.right <=w ? dirty.right : w; |
| b = dirty.bottom<=h ? dirty.bottom : h; |
| #else |
| l = t = 0; |
| r = w; |
| b = h; |
| #endif |
| |
| /* find the right page */ |
| int ypage = state->vinfo.yoffset; |
| |
| int x, y; |
| for (y = t ; y < b ; y++) { |
| // no "stride" issues with this display |
| uint8_t* outPtr = dst + (y*w+l)*3; |
| const uint16_t* ptr16 = (uint16_t*)state->vramAddr + ((y+ypage)*w+l); |
| for (x = l; x < r; x++) { |
| uint16_t in = *ptr16++; |
| uint32_t R,G,B; |
| R = ((in>>8)&0xF8) | (in>>(8+5)); |
| G = (in & 0x7E0)>>3; |
| G |= G>>6; |
| B = (in & 0x1F)<<3; |
| B |= B>>5; |
| *outPtr++ = R; |
| *outPtr++ = G; |
| *outPtr++ = B; |
| } |
| } |
| |
| wsUnlockDisplay(state->displayIdx); |
| |
| /* notify the simulator */ |
| wsPostDisplayUpdate(state->displayIdx); |
| } |
| |
| /* |
| * Provide a memory-mapped region for framebuffer data. We want to use a |
| * real mmap() call, not fake it with a malloc, so that related calls |
| * (munmap, madvise) will just work. |
| */ |
| static void* mmapFb(FakeDev* dev, void* start, size_t length, int prot, |
| int flags, int fd, __off_t offset) |
| { |
| FbState* state = (FbState*) dev->state; |
| void* map; |
| |
| /* be reasonable */ |
| if (length > (640*480*2)*4) { |
| errno = EINVAL; |
| return MAP_FAILED; |
| } |
| |
| /* this is supposed to be VRAM, so just map a chunk */ |
| map = mmap(start, length, prot, MAP_PRIVATE | MAP_ANON, -1, 0); |
| |
| /* update our "VRAM address"; this feels a bit fragile */ |
| if (state->vramAddr != NULL) { |
| wsLog("%s: NOTE: changing vram address from %p\n", |
| dev->debugName, state->vramAddr); |
| } |
| state->vramAddr = map; |
| |
| wsLog("%s: mmap %u bytes --> %p\n", dev->debugName, length, map); |
| return map; |
| } |
| |
| /* |
| * Handle framebuffer ioctls. |
| */ |
| static int ioctlFb(FakeDev* dev, int fd, int request, void* argp) |
| { |
| FbState* state = (FbState*) dev->state; |
| |
| wsLog("%s: ioctl(0x%x, %p)\n", dev->debugName, request, argp); |
| |
| switch (request) { |
| case FBIOGET_FSCREENINFO: // struct fb_fix_screeninfo* |
| memcpy(argp, &state->finfo, sizeof(struct fb_fix_screeninfo)); |
| break; |
| case FBIOGET_VSCREENINFO: // struct fb_var_screeninfo* |
| memcpy(argp, &state->vinfo, sizeof(struct fb_var_screeninfo)); |
| break; |
| case FBIOPUT_VSCREENINFO: // struct fb_var_screeninfo* |
| memcpy(&state->vinfo, argp, sizeof(struct fb_var_screeninfo)); |
| if (state->vinfo.activate == FB_ACTIVATE_NOW) { |
| //wsLog("%s: activate now\n", dev->debugName); |
| sendPixelsToSim(state); |
| } else if (state->vinfo.activate == FB_ACTIVATE_VBL) { |
| //wsLog("%s: activate on VBL\n", dev->debugName); |
| sendPixelsToSim(state); |
| /* we wait *after* so other process gets scheduled to draw */ |
| waitForVsync(state); |
| } else { |
| wsLog("%s: activate value is %d\n", |
| dev->debugName, state->vinfo.activate); |
| } |
| break; |
| case FBIOGET_VBLANK: // struct fb_vblank* |
| /* the device doesn't actually implement this */ |
| //memset(argp, 0, sizeof(struct fb_vblank)); |
| errno = EINVAL; |
| return -1; |
| default: |
| /*case FBIO_WAITFORVSYNC:*/ |
| wsLog("GLITCH: UNKNOWN ioctl request 0x%x on %s\n", |
| request, dev->debugName); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * Free up our state before closing down the fake descriptor. |
| */ |
| static int closeFb(FakeDev* dev, int fd) |
| { |
| freeState((FbState*)dev->state); |
| dev->state = NULL; |
| return 0; |
| } |
| |
| /* |
| * Open the console TTY device, which responds to a collection of ioctl()s. |
| */ |
| FakeDev* wsOpenDevFb(const char* pathName, int flags) |
| { |
| FakeDev* newDev = wsCreateFakeDev(pathName); |
| if (newDev != NULL) { |
| newDev->mmap = mmapFb; |
| newDev->ioctl = ioctlFb; |
| newDev->close = closeFb; |
| |
| FbState* fbState = calloc(1, sizeof(FbState)); |
| |
| /* establish a connection to the front-end if necessary */ |
| /* (also gets display configuration) */ |
| wsSimConnect(); |
| |
| configureInitialState(0, fbState); // always use display 0 for now |
| newDev->state = fbState; |
| } |
| |
| return newDev; |
| } |
| |