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