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
18 /* refcount for dup() */
19 int refCount;
20
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 */
configureInitialState(int displayIdx,FbState * fbState)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 */
freeState(FbState * fbState)82 static void freeState(FbState* fbState)
83 {
84 int oldcount;
85
86 oldcount = wsAtomicAdd(&fbState->refCount, -1);
87
88 if (oldcount == 0) {
89 free(fbState);
90 }
91 }
92
93 /*
94 * Wait for our synthetic vsync to happen.
95 */
waitForVsync(FbState * state)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 */
sendPixelsToSim(FbState * state)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 */
mmapFb(FakeDev * dev,void * start,size_t length,int prot,int flags,int fd,__off_t offset)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 */
ioctlFb(FakeDev * dev,int fd,int request,void * argp)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 */
closeFb(FakeDev * dev,int fd)247 static int closeFb(FakeDev* dev, int fd)
248 {
249 freeState((FbState*)dev->state);
250 dev->state = NULL;
251 return 0;
252 }
253
254 /*
255 * dup() an existing fake descriptor
256 */
dupFb(FakeDev * dev,int fd)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 /*
277 * Open the console TTY device, which responds to a collection of ioctl()s.
278 */
wsOpenDevFb(const char * pathName,int flags)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;
286 newDev->dup = dupFb;
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
301