blob: 7d2494eb49951593fe01ab7a97135ac3139bb52d [file] [log] [blame]
The Android Open Source Project52d4c302009-03-03 19:29:09 -08001/*
2 * Copyright 2007 The Android Open Source Project
3 *
4 * Fake device support.
5 */
6/*
7Implementation notes:
8
9There are a couple of basic scenarios, exemplified by the "fb" and
10"events" devices. The framebuffer driver is pretty simple, handling a
11few ioctl()s and managing a stretch of memory. We can just intercept a
12few calls. The input event driver can be used in a select() or poll()
13call with other file descriptors, which either requires us to do some
14fancy tricks with select() and poll(), or requires that we return a real
15file descriptor (perhaps based on a socketpair).
16
17We have three basic approaches to dealing with "fake" file descriptors:
18
19(1) Always use real fds. We can dup() an open /dev/null to get a number
20 for the cases where we don't need a socketpair.
21(2) Always use fake fds with absurdly high numeric values. Testing to see
22 if the fd is one we handle is trivial (range check). This doesn't
23 work for select(), which uses fd bitmaps accessed through macros.
24(3) Use a mix of real and fake fds, in a high range (512-1023). Because
25 it's in the "real" range, we can pass real fds around for things that
26 are handed to poll() and select(), but because of the high numeric
27 value we *should* be able to get away with a trivial range check.
28
29Approach (1) is the most portable and least likely to break, but the
30efficiencies gained in approach (2) make it more desirable. There is
31a small risk of application fds wandering into our range, but we can
32minimize that by asserting on a "guard zone" and/or obstructing dup2().
33(We can also dup2(/dev/null) to "reserve" our fds, but that wastes
34resources.)
35*/
36
37#include "Common.h"
38
39#include <stdlib.h>
40#include <string.h>
41#include <sys/types.h>
42#include <sys/socket.h>
43#include <assert.h>
44#include <fnmatch.h>
45
46/*
47 * Devices we intercept.
48 *
49 * Needed:
50 * /dev/alarm
51 * radio
52 */
53typedef FakeDev* (*wsFileHook)(const char *path, int flags);
54
55typedef struct FakedPath {
56 const char *pathexpr;
57 wsFileHook hook;
58} FakedPath;
59
60FakedPath fakedpaths[] =
61{
62 { "/dev/graphics/fb0", wsOpenDevFb },
63 { "/dev/hw3d", NULL },
64 { "/dev/eac", wsOpenDevAudio },
65 { "/dev/tty0", wsOpenDevConsoleTty },
66 { "/dev/input/event0", wsOpenDevEvent },
67 { "/dev/input/*", NULL },
68 { "/dev/log/*", wsOpenDevLog },
69 { "/sys/class/power_supply/*", wsOpenDevPower },
The Android Open Source Project700ccfc2009-03-18 22:20:25 -070070 { "/sys/power/state", wsOpenSysPower },
71 { "/sys/power/wake_lock", wsOpenSysPower },
72 { "/sys/power/wake_unlock", wsOpenSysPower },
The Android Open Source Project52d4c302009-03-03 19:29:09 -080073 { "/sys/devices/platform/android-vibrator/enable", wsOpenDevVibrator },
74 { "/sys/qemu_trace/*", NULL },
75 { NULL, NULL }
76};
77
78
79/*
80 * Generic drop-in for an unimplemented call.
81 *
82 * Returns -1, which conveniently is the same as MAP_FAILED for mmap.
83 */
84static int notImplemented(FakeDev* dev, const char* callName)
85{
86 wsLog("WARNING: unimplemented %s() on '%s' %p\n",
87 callName, dev->debugName, dev->state);
88 errno = kNoHandlerError;
89 return -1;
90}
91
92/*
93 * Default implementations. We want to log as much information as we can
94 * so that we can fill in the missing implementation.
95 *
96 * TODO: for some or all of these we will want to display the full arg list.
97 */
98static int noClose(FakeDev* dev, ...)
99{
100 return 0;
101}
102static int noRead(FakeDev* dev, ...)
103{
104 return notImplemented(dev, "read");
105}
106static int noReadv(FakeDev* dev, ...)
107{
108 return notImplemented(dev, "readv");
109}
110static int noWrite(FakeDev* dev, ...)
111{
112 return notImplemented(dev, "write");
113}
114static int noWritev(FakeDev* dev, ...)
115{
116 return notImplemented(dev, "writev");
117}
118static int noMmap(FakeDev* dev, ...)
119{
120 return notImplemented(dev, "mmap");
121}
122static int noIoctl(FakeDev* dev, ...)
123{
124 return notImplemented(dev, "ioctl");
125}
126
127
128/*
129 * Create a new FakeDev entry.
130 *
131 * We mark the fd slot as "used" in the bitmap, but don't add it to the
132 * table yet since the entry is not fully prepared.
133 */
134FakeDev* wsCreateFakeDev(const char* debugName)
135{
136 FakeDev* newDev;
137 int cc;
138
139 assert(debugName != NULL);
140
141 newDev = (FakeDev*) calloc(1, sizeof(FakeDev));
142 if (newDev == NULL)
143 return NULL;
144
145 newDev->debugName = strdup(debugName);
146 newDev->state = NULL;
147
148 newDev->close = (Fake_close) noClose;
149 newDev->read = (Fake_read) noRead;
150 newDev->readv = (Fake_readv) noReadv;
151 newDev->write = (Fake_write) noWrite;
152 newDev->writev = (Fake_writev) noWritev;
153 newDev->mmap = (Fake_mmap) noMmap;
154 newDev->ioctl = (Fake_ioctl) noIoctl;
155
156 /*
157 * Allocate a new entry. The bit vector map is really only used as a
158 * performance boost in the current implementation.
159 */
160 cc = pthread_mutex_lock(&gWrapSim.fakeFdLock); assert(cc == 0);
161 int newfd = wsAllocBit(gWrapSim.fakeFdMap);
162 cc = pthread_mutex_unlock(&gWrapSim.fakeFdLock); assert(cc == 0);
163
164 if (newfd < 0) {
165 wsLog("WARNING: ran out of 'fake' file descriptors\n");
166 free(newDev);
167 return NULL;
168 }
169 newDev->fd = newfd + kFakeFdBase;
170 newDev->otherFd = -1;
171 assert(gWrapSim.fakeFdList[newDev->fd - kFakeFdBase] == NULL);
172
173 return newDev;
174}
175
176/*
177 * Create a new FakeDev entry, and open a file descriptor that actually
178 * works.
179 */
180FakeDev* wsCreateRealFakeDev(const char* debugName)
181{
182 FakeDev* newDev = wsCreateFakeDev(debugName);
183 if (newDev == NULL)
184 return newDev;
185
186 int fds[2];
187
188 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
189 wsLog("socketpair() failed: %s\n", strerror(errno));
190 wsFreeFakeDev(newDev);
191 return NULL;
192 }
193
194 if (dup2(fds[0], newDev->fd) < 0) {
195 wsLog("dup2(%d,%d) failed: %s\n",
196 fds[0], newDev->fd, strerror(errno));
197 wsFreeFakeDev(newDev);
198 return NULL;
199 }
200 close(fds[0]);
201
202 /* okay to leave this one in the "normal" range; not visible to app */
203 newDev->otherFd = fds[1];
204
205 return newDev;
206}
207
208/*
209 * Free fake device entry.
210 */
211void wsFreeFakeDev(FakeDev* dev)
212{
213 if (dev == NULL)
214 return;
215
216 wsLog("## closing/freeing '%s' (%d/%d)\n",
217 dev->debugName, dev->fd, dev->otherFd);
218
219 /*
220 * If we assigned a file descriptor slot, free it up.
221 */
222 if (dev->fd >= 0) {
223 int cc;
224
225 gWrapSim.fakeFdList[dev->fd - kFakeFdBase] = NULL;
226
227 cc = pthread_mutex_lock(&gWrapSim.fakeFdLock); assert(cc == 0);
228 wsFreeBit(gWrapSim.fakeFdMap, dev->fd - kFakeFdBase);
229 cc = pthread_mutex_unlock(&gWrapSim.fakeFdLock); assert(cc == 0);
230 }
231 if (dev->otherFd >= 0)
232 close(dev->otherFd);
233
234 if (dev->debugName) free(dev->debugName);
235 free(dev);
236}
237
238/*
239 * Map a file descriptor to a fake device.
240 *
241 * Returns NULL if there's no corresponding entry.
242 */
243FakeDev* wsFakeDevFromFd(int fd)
244{
245 /* quick range test */
246 if (fd < kFakeFdBase || fd >= kFakeFdBase + kMaxFakeFdCount)
247 return NULL;
248
249 return gWrapSim.fakeFdList[fd - kFakeFdBase];
250}
251
252
253/*
254 * Check to see if we're opening a device that we want to fake out.
255 *
256 * We return a file descriptor >= 0 on success, -1 if we're not interested,
257 * or -2 if we explicitly want to pretend that the device doesn't exist.
258 */
259int wsInterceptDeviceOpen(const char* pathName, int flags)
260{
261 FakedPath* p = fakedpaths;
262
263 while (p->pathexpr) {
264 if (fnmatch(p->pathexpr, pathName, 0) == 0) {
265 if (p->hook != NULL) {
266 FakeDev* dev = p->hook(pathName, flags);
267 if (dev != NULL) {
268 /*
269 * Now that the device entry is ready, add it to the list.
270 */
271 wsLog("## created fake dev %d: '%s' %p\n",
272 dev->fd, dev->debugName, dev->state);
273 gWrapSim.fakeFdList[dev->fd - kFakeFdBase] = dev;
274 return dev->fd;
275 }
276 } else {
277 wsLog("## rejecting attempt to open %s\n", pathName);
278 errno = ENOENT;
279 return -2;
280 }
281 break;
282 }
283 p++;
284 }
285 return -1;
286}
287
288/*
289 * Check to see if we're accessing a device that we want to fake out.
290 * Returns 0 if the device can be (fake) opened with the given mode,
291 * -1 if it can't, -2 if it can't and we don't want to allow fallback
292 * to the host-device either.
293 * TODO: actually check the mode.
294 */
295int wsInterceptDeviceAccess(const char *pathName, int mode)
296{
297 FakedPath *p = fakedpaths;
298
299 while (p->pathexpr) {
300 if (fnmatch(p->pathexpr, pathName, 0) == 0) {
301 if (p->hook) {
302 return 0;
303 } else {
304 wsLog("## rejecting attempt to open %s\n", pathName);
305 errno = ENOENT;
306 return -2;
307 }
308 break;
309 }
310 p++;
311 }
312 errno = ENOENT;
313 return -1;
314}