• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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