The Android Open Source Project | 52d4c30 | 2009-03-03 19:29:09 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2007 The Android Open Source Project |
| 3 | * |
| 4 | * Fake device support. |
| 5 | */ |
| 6 | #include "Common.h" |
| 7 | |
| 8 | #include <stdlib.h> |
| 9 | #include <string.h> |
| 10 | #include <assert.h> |
| 11 | |
| 12 | #include <sys/mman.h> |
| 13 | #include <sys/ioctl.h> |
| 14 | #include <linux/fb.h> |
| 15 | |
| 16 | typedef struct FbState { |
| 17 | /* index into gWrapSim.display[] */ |
| 18 | int displayIdx; |
| 19 | |
| 20 | /* VRAM address, set by mmap() call */ |
| 21 | void* vramAddr; |
| 22 | |
| 23 | /* kernel data structures */ |
| 24 | struct fb_var_screeninfo vinfo; |
| 25 | struct fb_fix_screeninfo finfo; |
| 26 | } FbState; |
| 27 | |
| 28 | |
| 29 | /* |
| 30 | * Set up the initial values of the structs. |
| 31 | * |
| 32 | * The FbState struct is zeroed out initially, so we only need to set the |
| 33 | * fields that don't default to zero. |
| 34 | */ |
| 35 | static void configureInitialState(int displayIdx, FbState* fbState) |
| 36 | { |
| 37 | int width, height; |
| 38 | |
| 39 | assert(displayIdx >= 0 && displayIdx < gWrapSim.numDisplays); |
| 40 | |
| 41 | width = gWrapSim.display[displayIdx].width; |
| 42 | height = gWrapSim.display[displayIdx].height; |
| 43 | wsLog("Configuring FbState for display %d (%dx%x key=0x%08x)\n", |
| 44 | displayIdx, width, height, gWrapSim.display[displayIdx].shmemKey); |
| 45 | |
| 46 | /* fb_fix_screeninfo */ |
| 47 | strcpy(fbState->finfo.id, "omapfb"); |
| 48 | fbState->finfo.smem_len = (width * 2) * height * 2; |
| 49 | fbState->finfo.line_length = width * 2; |
| 50 | |
| 51 | /* fb_var_screeninfo */ |
| 52 | fbState->vinfo.xres = width; |
| 53 | fbState->vinfo.yres = height; |
| 54 | fbState->vinfo.xres_virtual = width; |
| 55 | fbState->vinfo.yres_virtual = height * 2; |
| 56 | fbState->vinfo.bits_per_pixel = 16; |
| 57 | |
| 58 | fbState->vinfo.red.offset = 11; |
| 59 | fbState->vinfo.red.length = 5; |
| 60 | fbState->vinfo.green.offset = 5; |
| 61 | fbState->vinfo.green.length = 6; |
| 62 | fbState->vinfo.blue.offset = 0; |
| 63 | fbState->vinfo.blue.length = 5; |
| 64 | |
| 65 | fbState->vinfo.width = 51; // physical dimension, used for dpi |
| 66 | fbState->vinfo.height = 76; |
| 67 | |
| 68 | fbState->vinfo.pixclock = 103092; |
| 69 | fbState->vinfo.upper_margin = 3; |
| 70 | fbState->vinfo.lower_margin = 227; |
| 71 | fbState->vinfo.left_margin = 12; |
| 72 | fbState->vinfo.right_margin = 8; |
| 73 | } |
| 74 | |
| 75 | /* |
| 76 | * Free allocated state. |
| 77 | */ |
| 78 | static void freeState(FbState* fbState) |
| 79 | { |
| 80 | free(fbState); |
| 81 | } |
| 82 | |
| 83 | /* |
| 84 | * Wait for our synthetic vsync to happen. |
| 85 | */ |
| 86 | static void waitForVsync(FbState* state) |
| 87 | { |
| 88 | /* TODO: simulate a real interval */ |
| 89 | usleep(1000000/60); |
| 90 | } |
| 91 | |
| 92 | /* |
| 93 | * Forward pixels to the simulator. |
| 94 | */ |
| 95 | static void sendPixelsToSim(FbState* state) |
| 96 | { |
| 97 | if (state->vramAddr == 0) { |
| 98 | wsLog("## not sending pixels (no addr yet)\n"); |
| 99 | return; |
| 100 | } |
| 101 | |
| 102 | //wsLog("+++ sending pixels to sim (disp=%d yoff=%d)\n", |
| 103 | // state->displayIdx, state->vinfo.yoffset); |
| 104 | |
| 105 | wsLockDisplay(state->displayIdx); |
| 106 | |
| 107 | uint8_t* dst = gWrapSim.display[state->displayIdx].addr; |
| 108 | |
| 109 | int l,t,r,b,w,h; |
| 110 | w = gWrapSim.display[state->displayIdx].width; |
| 111 | h = gWrapSim.display[state->displayIdx].height; |
| 112 | |
| 113 | #if 0 |
| 114 | /* |
| 115 | * TODO: surfaceflinger encodes the dirty region in vinfo.reserved[]. We |
| 116 | * can use that to perform a partial update. |
| 117 | */ |
| 118 | const Rect dirty(dirtyReg.bounds()); |
| 119 | l = dirty.left >=0 ? dirty.left : 0; |
| 120 | t = dirty.top >=0 ? dirty.top : 0; |
| 121 | r = dirty.right <=w ? dirty.right : w; |
| 122 | b = dirty.bottom<=h ? dirty.bottom : h; |
| 123 | #else |
| 124 | l = t = 0; |
| 125 | r = w; |
| 126 | b = h; |
| 127 | #endif |
| 128 | |
| 129 | /* find the right page */ |
| 130 | int ypage = state->vinfo.yoffset; |
| 131 | |
| 132 | int x, y; |
| 133 | for (y = t ; y < b ; y++) { |
| 134 | // no "stride" issues with this display |
| 135 | uint8_t* outPtr = dst + (y*w+l)*3; |
| 136 | const uint16_t* ptr16 = (uint16_t*)state->vramAddr + ((y+ypage)*w+l); |
| 137 | for (x = l; x < r; x++) { |
| 138 | uint16_t in = *ptr16++; |
| 139 | uint32_t R,G,B; |
| 140 | R = ((in>>8)&0xF8) | (in>>(8+5)); |
| 141 | G = (in & 0x7E0)>>3; |
| 142 | G |= G>>6; |
| 143 | B = (in & 0x1F)<<3; |
| 144 | B |= B>>5; |
| 145 | *outPtr++ = R; |
| 146 | *outPtr++ = G; |
| 147 | *outPtr++ = B; |
| 148 | } |
| 149 | } |
| 150 | |
| 151 | wsUnlockDisplay(state->displayIdx); |
| 152 | |
| 153 | /* notify the simulator */ |
| 154 | wsPostDisplayUpdate(state->displayIdx); |
| 155 | } |
| 156 | |
| 157 | /* |
| 158 | * Provide a memory-mapped region for framebuffer data. We want to use a |
| 159 | * real mmap() call, not fake it with a malloc, so that related calls |
| 160 | * (munmap, madvise) will just work. |
| 161 | */ |
| 162 | static void* mmapFb(FakeDev* dev, void* start, size_t length, int prot, |
| 163 | int flags, int fd, __off_t offset) |
| 164 | { |
| 165 | FbState* state = (FbState*) dev->state; |
| 166 | void* map; |
| 167 | |
| 168 | /* be reasonable */ |
| 169 | if (length > (640*480*2)*4) { |
| 170 | errno = EINVAL; |
| 171 | return MAP_FAILED; |
| 172 | } |
| 173 | |
| 174 | /* this is supposed to be VRAM, so just map a chunk */ |
| 175 | map = mmap(start, length, prot, MAP_PRIVATE | MAP_ANON, -1, 0); |
| 176 | |
| 177 | /* update our "VRAM address"; this feels a bit fragile */ |
| 178 | if (state->vramAddr != NULL) { |
| 179 | wsLog("%s: NOTE: changing vram address from %p\n", |
| 180 | dev->debugName, state->vramAddr); |
| 181 | } |
| 182 | state->vramAddr = map; |
| 183 | |
| 184 | wsLog("%s: mmap %u bytes --> %p\n", dev->debugName, length, map); |
| 185 | return map; |
| 186 | } |
| 187 | |
| 188 | /* |
| 189 | * Handle framebuffer ioctls. |
| 190 | */ |
| 191 | static int ioctlFb(FakeDev* dev, int fd, int request, void* argp) |
| 192 | { |
| 193 | FbState* state = (FbState*) dev->state; |
| 194 | |
| 195 | wsLog("%s: ioctl(0x%x, %p)\n", dev->debugName, request, argp); |
| 196 | |
| 197 | switch (request) { |
| 198 | case FBIOGET_FSCREENINFO: // struct fb_fix_screeninfo* |
| 199 | memcpy(argp, &state->finfo, sizeof(struct fb_fix_screeninfo)); |
| 200 | break; |
| 201 | case FBIOGET_VSCREENINFO: // struct fb_var_screeninfo* |
| 202 | memcpy(argp, &state->vinfo, sizeof(struct fb_var_screeninfo)); |
| 203 | break; |
| 204 | case FBIOPUT_VSCREENINFO: // struct fb_var_screeninfo* |
| 205 | memcpy(&state->vinfo, argp, sizeof(struct fb_var_screeninfo)); |
| 206 | if (state->vinfo.activate == FB_ACTIVATE_NOW) { |
| 207 | //wsLog("%s: activate now\n", dev->debugName); |
| 208 | sendPixelsToSim(state); |
| 209 | } else if (state->vinfo.activate == FB_ACTIVATE_VBL) { |
| 210 | //wsLog("%s: activate on VBL\n", dev->debugName); |
| 211 | sendPixelsToSim(state); |
| 212 | /* we wait *after* so other process gets scheduled to draw */ |
| 213 | waitForVsync(state); |
| 214 | } else { |
| 215 | wsLog("%s: activate value is %d\n", |
| 216 | dev->debugName, state->vinfo.activate); |
| 217 | } |
| 218 | break; |
| 219 | case FBIOGET_VBLANK: // struct fb_vblank* |
| 220 | /* the device doesn't actually implement this */ |
| 221 | //memset(argp, 0, sizeof(struct fb_vblank)); |
| 222 | errno = EINVAL; |
| 223 | return -1; |
| 224 | default: |
| 225 | /*case FBIO_WAITFORVSYNC:*/ |
| 226 | wsLog("GLITCH: UNKNOWN ioctl request 0x%x on %s\n", |
| 227 | request, dev->debugName); |
| 228 | return -1; |
| 229 | } |
| 230 | |
| 231 | return 0; |
| 232 | } |
| 233 | |
| 234 | /* |
| 235 | * Free up our state before closing down the fake descriptor. |
| 236 | */ |
| 237 | static int closeFb(FakeDev* dev, int fd) |
| 238 | { |
| 239 | freeState((FbState*)dev->state); |
| 240 | dev->state = NULL; |
| 241 | return 0; |
| 242 | } |
| 243 | |
| 244 | /* |
| 245 | * Open the console TTY device, which responds to a collection of ioctl()s. |
| 246 | */ |
| 247 | FakeDev* wsOpenDevFb(const char* pathName, int flags) |
| 248 | { |
| 249 | FakeDev* newDev = wsCreateFakeDev(pathName); |
| 250 | if (newDev != NULL) { |
| 251 | newDev->mmap = mmapFb; |
| 252 | newDev->ioctl = ioctlFb; |
| 253 | newDev->close = closeFb; |
| 254 | |
| 255 | FbState* fbState = calloc(1, sizeof(FbState)); |
| 256 | |
| 257 | /* establish a connection to the front-end if necessary */ |
| 258 | /* (also gets display configuration) */ |
| 259 | wsSimConnect(); |
| 260 | |
| 261 | configureInitialState(0, fbState); // always use display 0 for now |
| 262 | newDev->state = fbState; |
| 263 | } |
| 264 | |
| 265 | return newDev; |
| 266 | } |
| 267 | |