blob: 3e223d36361e5d3b6e70463d3ab22d64491916cb [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 },
70 { "/sys/devices/platform/android-vibrator/enable", wsOpenDevVibrator },
71 { "/sys/qemu_trace/*", NULL },
72 { NULL, NULL }
73};
74
75
76/*
77 * Generic drop-in for an unimplemented call.
78 *
79 * Returns -1, which conveniently is the same as MAP_FAILED for mmap.
80 */
81static int notImplemented(FakeDev* dev, const char* callName)
82{
83 wsLog("WARNING: unimplemented %s() on '%s' %p\n",
84 callName, dev->debugName, dev->state);
85 errno = kNoHandlerError;
86 return -1;
87}
88
89/*
90 * Default implementations. We want to log as much information as we can
91 * so that we can fill in the missing implementation.
92 *
93 * TODO: for some or all of these we will want to display the full arg list.
94 */
95static int noClose(FakeDev* dev, ...)
96{
97 return 0;
98}
99static int noRead(FakeDev* dev, ...)
100{
101 return notImplemented(dev, "read");
102}
103static int noReadv(FakeDev* dev, ...)
104{
105 return notImplemented(dev, "readv");
106}
107static int noWrite(FakeDev* dev, ...)
108{
109 return notImplemented(dev, "write");
110}
111static int noWritev(FakeDev* dev, ...)
112{
113 return notImplemented(dev, "writev");
114}
115static int noMmap(FakeDev* dev, ...)
116{
117 return notImplemented(dev, "mmap");
118}
119static int noIoctl(FakeDev* dev, ...)
120{
121 return notImplemented(dev, "ioctl");
122}
123
124
125/*
126 * Create a new FakeDev entry.
127 *
128 * We mark the fd slot as "used" in the bitmap, but don't add it to the
129 * table yet since the entry is not fully prepared.
130 */
131FakeDev* wsCreateFakeDev(const char* debugName)
132{
133 FakeDev* newDev;
134 int cc;
135
136 assert(debugName != NULL);
137
138 newDev = (FakeDev*) calloc(1, sizeof(FakeDev));
139 if (newDev == NULL)
140 return NULL;
141
142 newDev->debugName = strdup(debugName);
143 newDev->state = NULL;
144
145 newDev->close = (Fake_close) noClose;
146 newDev->read = (Fake_read) noRead;
147 newDev->readv = (Fake_readv) noReadv;
148 newDev->write = (Fake_write) noWrite;
149 newDev->writev = (Fake_writev) noWritev;
150 newDev->mmap = (Fake_mmap) noMmap;
151 newDev->ioctl = (Fake_ioctl) noIoctl;
152
153 /*
154 * Allocate a new entry. The bit vector map is really only used as a
155 * performance boost in the current implementation.
156 */
157 cc = pthread_mutex_lock(&gWrapSim.fakeFdLock); assert(cc == 0);
158 int newfd = wsAllocBit(gWrapSim.fakeFdMap);
159 cc = pthread_mutex_unlock(&gWrapSim.fakeFdLock); assert(cc == 0);
160
161 if (newfd < 0) {
162 wsLog("WARNING: ran out of 'fake' file descriptors\n");
163 free(newDev);
164 return NULL;
165 }
166 newDev->fd = newfd + kFakeFdBase;
167 newDev->otherFd = -1;
168 assert(gWrapSim.fakeFdList[newDev->fd - kFakeFdBase] == NULL);
169
170 return newDev;
171}
172
173/*
174 * Create a new FakeDev entry, and open a file descriptor that actually
175 * works.
176 */
177FakeDev* wsCreateRealFakeDev(const char* debugName)
178{
179 FakeDev* newDev = wsCreateFakeDev(debugName);
180 if (newDev == NULL)
181 return newDev;
182
183 int fds[2];
184
185 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
186 wsLog("socketpair() failed: %s\n", strerror(errno));
187 wsFreeFakeDev(newDev);
188 return NULL;
189 }
190
191 if (dup2(fds[0], newDev->fd) < 0) {
192 wsLog("dup2(%d,%d) failed: %s\n",
193 fds[0], newDev->fd, strerror(errno));
194 wsFreeFakeDev(newDev);
195 return NULL;
196 }
197 close(fds[0]);
198
199 /* okay to leave this one in the "normal" range; not visible to app */
200 newDev->otherFd = fds[1];
201
202 return newDev;
203}
204
205/*
206 * Free fake device entry.
207 */
208void wsFreeFakeDev(FakeDev* dev)
209{
210 if (dev == NULL)
211 return;
212
213 wsLog("## closing/freeing '%s' (%d/%d)\n",
214 dev->debugName, dev->fd, dev->otherFd);
215
216 /*
217 * If we assigned a file descriptor slot, free it up.
218 */
219 if (dev->fd >= 0) {
220 int cc;
221
222 gWrapSim.fakeFdList[dev->fd - kFakeFdBase] = NULL;
223
224 cc = pthread_mutex_lock(&gWrapSim.fakeFdLock); assert(cc == 0);
225 wsFreeBit(gWrapSim.fakeFdMap, dev->fd - kFakeFdBase);
226 cc = pthread_mutex_unlock(&gWrapSim.fakeFdLock); assert(cc == 0);
227 }
228 if (dev->otherFd >= 0)
229 close(dev->otherFd);
230
231 if (dev->debugName) free(dev->debugName);
232 free(dev);
233}
234
235/*
236 * Map a file descriptor to a fake device.
237 *
238 * Returns NULL if there's no corresponding entry.
239 */
240FakeDev* wsFakeDevFromFd(int fd)
241{
242 /* quick range test */
243 if (fd < kFakeFdBase || fd >= kFakeFdBase + kMaxFakeFdCount)
244 return NULL;
245
246 return gWrapSim.fakeFdList[fd - kFakeFdBase];
247}
248
249
250/*
251 * Check to see if we're opening a device that we want to fake out.
252 *
253 * We return a file descriptor >= 0 on success, -1 if we're not interested,
254 * or -2 if we explicitly want to pretend that the device doesn't exist.
255 */
256int wsInterceptDeviceOpen(const char* pathName, int flags)
257{
258 FakedPath* p = fakedpaths;
259
260 while (p->pathexpr) {
261 if (fnmatch(p->pathexpr, pathName, 0) == 0) {
262 if (p->hook != NULL) {
263 FakeDev* dev = p->hook(pathName, flags);
264 if (dev != NULL) {
265 /*
266 * Now that the device entry is ready, add it to the list.
267 */
268 wsLog("## created fake dev %d: '%s' %p\n",
269 dev->fd, dev->debugName, dev->state);
270 gWrapSim.fakeFdList[dev->fd - kFakeFdBase] = dev;
271 return dev->fd;
272 }
273 } else {
274 wsLog("## rejecting attempt to open %s\n", pathName);
275 errno = ENOENT;
276 return -2;
277 }
278 break;
279 }
280 p++;
281 }
282 return -1;
283}
284
285/*
286 * Check to see if we're accessing a device that we want to fake out.
287 * Returns 0 if the device can be (fake) opened with the given mode,
288 * -1 if it can't, -2 if it can't and we don't want to allow fallback
289 * to the host-device either.
290 * TODO: actually check the mode.
291 */
292int wsInterceptDeviceAccess(const char *pathName, int mode)
293{
294 FakedPath *p = fakedpaths;
295
296 while (p->pathexpr) {
297 if (fnmatch(p->pathexpr, pathName, 0) == 0) {
298 if (p->hook) {
299 return 0;
300 } else {
301 wsLog("## rejecting attempt to open %s\n", pathName);
302 errno = ENOENT;
303 return -2;
304 }
305 break;
306 }
307 p++;
308 }
309 errno = ENOENT;
310 return -1;
311}