blob: 8e912de187726ab3ccae1f7890bdeae307c46385 [file] [log] [blame]
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -07001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <errno.h>
18#include <stdio.h>
19#include <stdlib.h>
20#include <sys/stat.h>
21#include <sys/types.h>
22
23#include <fcntl.h>
24#include <dirent.h>
25#include <unistd.h>
26#include <string.h>
27
28#include <sys/socket.h>
29#include <sys/un.h>
30#include <linux/netlink.h>
31#include <private/android_filesystem_config.h>
32#include <sys/time.h>
33#include <asm/page.h>
Colin Cross982a8152010-06-03 12:21:01 -070034#include <sys/wait.h>
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070035
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070036#include "devices.h"
Colin Crossb0ab94b2010-04-08 16:16:20 -070037#include "util.h"
Colin Crossed8a7d82010-04-19 17:05:34 -070038#include "log.h"
39#include "list.h"
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070040
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070041#define SYSFS_PREFIX "/sys"
42#define FIRMWARE_DIR "/etc/firmware"
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070043
Colin Cross0dd7ca62010-04-13 19:25:51 -070044static int device_fd = -1;
45
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070046struct uevent {
47 const char *action;
48 const char *path;
49 const char *subsystem;
50 const char *firmware;
Colin Crossb0ab94b2010-04-08 16:16:20 -070051 const char *partition_name;
52 int partition_num;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070053 int major;
54 int minor;
55};
56
57static int open_uevent_socket(void)
58{
59 struct sockaddr_nl addr;
60 int sz = 64*1024; // XXX larger? udev uses 16MB!
Nick Kralevich5f5d5c82010-07-19 14:31:20 -070061 int on = 1;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070062 int s;
63
64 memset(&addr, 0, sizeof(addr));
65 addr.nl_family = AF_NETLINK;
66 addr.nl_pid = getpid();
67 addr.nl_groups = 0xffffffff;
68
69 s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
70 if(s < 0)
71 return -1;
72
73 setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz));
Nick Kralevich5f5d5c82010-07-19 14:31:20 -070074 setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070075
76 if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
77 close(s);
78 return -1;
79 }
80
81 return s;
82}
83
84struct perms_ {
85 char *name;
86 mode_t perm;
87 unsigned int uid;
88 unsigned int gid;
89 unsigned short prefix;
90};
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070091
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070092struct perm_node {
93 struct perms_ dp;
94 struct listnode plist;
95};
Colin Cross44b65d02010-04-20 14:32:50 -070096static list_declare(dev_perms);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070097
98/*
99 * Permission override when in emulator mode, must be parsed before
100 * system properties is initalized.
101 */
Colin Cross44b65d02010-04-20 14:32:50 -0700102int add_dev_perms(const char *name, mode_t perm, unsigned int uid,
103 unsigned int gid, unsigned short prefix) {
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700104 int size;
Colin Cross44b65d02010-04-20 14:32:50 -0700105 char *tmp = 0;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700106 struct perm_node *node = malloc(sizeof (struct perm_node));
107 if (!node)
108 return -ENOMEM;
109
110 size = strlen(name) + 1;
111 if ((node->dp.name = malloc(size)) == NULL)
112 return -ENOMEM;
113
114 memcpy(node->dp.name, name, size);
115 node->dp.perm = perm;
116 node->dp.uid = uid;
117 node->dp.gid = gid;
118 node->dp.prefix = prefix;
119
Colin Cross44b65d02010-04-20 14:32:50 -0700120 list_add_tail(&dev_perms, &node->plist);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700121 return 0;
122}
123
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700124static int get_device_perm_inner(struct perms_ *perms, const char *path,
125 unsigned *uid, unsigned *gid, mode_t *perm)
126{
127 int i;
128 for(i = 0; perms[i].name; i++) {
129
130 if(perms[i].prefix) {
131 if(strncmp(path, perms[i].name, strlen(perms[i].name)))
132 continue;
133 } else {
134 if(strcmp(path, perms[i].name))
135 continue;
136 }
137 *uid = perms[i].uid;
138 *gid = perms[i].gid;
139 *perm = perms[i].perm;
140 return 0;
141 }
142 return -1;
143}
144
145/* First checks for emulator specific permissions specified in /proc/cmdline. */
146static mode_t get_device_perm(const char *path, unsigned *uid, unsigned *gid)
147{
148 mode_t perm;
Colin Cross44b65d02010-04-20 14:32:50 -0700149 struct listnode *node;
150 struct perm_node *perm_node;
151 struct perms_ *dp;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700152
Colin Cross44b65d02010-04-20 14:32:50 -0700153 /* search the perms list in reverse so that ueventd.$hardware can
154 * override ueventd.rc
155 */
156 list_for_each_reverse(node, &dev_perms) {
157 perm_node = node_to_item(node, struct perm_node, plist);
158 dp = &perm_node->dp;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700159
Colin Cross44b65d02010-04-20 14:32:50 -0700160 if (dp->prefix) {
161 if (strncmp(path, dp->name, strlen(dp->name)))
162 continue;
163 } else {
164 if (strcmp(path, dp->name))
165 continue;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700166 }
Colin Cross44b65d02010-04-20 14:32:50 -0700167 *uid = dp->uid;
168 *gid = dp->gid;
169 return dp->perm;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700170 }
Colin Cross44b65d02010-04-20 14:32:50 -0700171 /* Default if nothing found. */
172 *uid = 0;
173 *gid = 0;
174 return 0600;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700175}
176
177static void make_device(const char *path, int block, int major, int minor)
178{
179 unsigned uid;
180 unsigned gid;
181 mode_t mode;
182 dev_t dev;
183
184 if(major > 255 || minor > 255)
185 return;
186
187 mode = get_device_perm(path, &uid, &gid) | (block ? S_IFBLK : S_IFCHR);
188 dev = (major << 8) | minor;
Nick Pelly6405c692010-01-21 18:13:39 -0800189 /* Temporarily change egid to avoid race condition setting the gid of the
190 * device node. Unforunately changing the euid would prevent creation of
191 * some device nodes, so the uid has to be set with chown() and is still
192 * racy. Fixing the gid race at least fixed the issue with system_server
193 * opening dynamic input devices under the AID_INPUT gid. */
194 setegid(gid);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700195 mknod(path, mode, dev);
Nick Pelly6405c692010-01-21 18:13:39 -0800196 chown(path, uid, -1);
197 setegid(AID_ROOT);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700198}
199
Chuck Tuffli1e070842008-12-15 14:26:56 -0800200#if LOG_UEVENTS
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700201
202static inline suseconds_t get_usecs(void)
203{
204 struct timeval tv;
205 gettimeofday(&tv, 0);
206 return tv.tv_sec * (suseconds_t) 1000000 + tv.tv_usec;
207}
208
209#define log_event_print(x...) INFO(x)
210
211#else
212
213#define log_event_print(fmt, args...) do { } while (0)
214#define get_usecs() 0
215
216#endif
217
218static void parse_event(const char *msg, struct uevent *uevent)
219{
220 uevent->action = "";
221 uevent->path = "";
222 uevent->subsystem = "";
223 uevent->firmware = "";
224 uevent->major = -1;
225 uevent->minor = -1;
Colin Crossb0ab94b2010-04-08 16:16:20 -0700226 uevent->partition_name = NULL;
227 uevent->partition_num = -1;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700228
229 /* currently ignoring SEQNUM */
230 while(*msg) {
231 if(!strncmp(msg, "ACTION=", 7)) {
232 msg += 7;
233 uevent->action = msg;
234 } else if(!strncmp(msg, "DEVPATH=", 8)) {
235 msg += 8;
236 uevent->path = msg;
237 } else if(!strncmp(msg, "SUBSYSTEM=", 10)) {
238 msg += 10;
239 uevent->subsystem = msg;
240 } else if(!strncmp(msg, "FIRMWARE=", 9)) {
241 msg += 9;
242 uevent->firmware = msg;
243 } else if(!strncmp(msg, "MAJOR=", 6)) {
244 msg += 6;
245 uevent->major = atoi(msg);
246 } else if(!strncmp(msg, "MINOR=", 6)) {
247 msg += 6;
248 uevent->minor = atoi(msg);
Colin Crossb0ab94b2010-04-08 16:16:20 -0700249 } else if(!strncmp(msg, "PARTN=", 6)) {
250 msg += 6;
251 uevent->partition_num = atoi(msg);
252 } else if(!strncmp(msg, "PARTNAME=", 9)) {
253 msg += 9;
254 uevent->partition_name = msg;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700255 }
256
257 /* advance to after the next \0 */
258 while(*msg++)
259 ;
260 }
261
262 log_event_print("event { '%s', '%s', '%s', '%s', %d, %d }\n",
263 uevent->action, uevent->path, uevent->subsystem,
264 uevent->firmware, uevent->major, uevent->minor);
265}
266
Colin Crossb0ab94b2010-04-08 16:16:20 -0700267static char **parse_platform_block_device(struct uevent *uevent)
268{
269 const char *driver;
270 const char *path;
271 char *slash;
272 int width;
273 char buf[256];
274 char link_path[256];
275 int fd;
276 int link_num = 0;
277 int ret;
278 char *p;
279 unsigned int size;
280 struct stat info;
281
282 char **links = malloc(sizeof(char *) * 4);
283 if (!links)
284 return NULL;
285 memset(links, 0, sizeof(char *) * 4);
286
287 /* Drop "/devices/platform/" */
288 path = uevent->path;
289 driver = path + 18;
290 slash = strchr(driver, '/');
291 if (!slash)
292 goto err;
293 width = slash - driver;
294 if (width <= 0)
295 goto err;
296
297 snprintf(link_path, sizeof(link_path), "/dev/block/platform/%.*s",
298 width, driver);
299
300 if (uevent->partition_name) {
301 p = strdup(uevent->partition_name);
302 sanitize(p);
303 if (asprintf(&links[link_num], "%s/by-name/%s", link_path, p) > 0)
304 link_num++;
305 else
306 links[link_num] = NULL;
307 free(p);
308 }
309
310 if (uevent->partition_num >= 0) {
311 if (asprintf(&links[link_num], "%s/by-num/p%d", link_path, uevent->partition_num) > 0)
312 link_num++;
313 else
314 links[link_num] = NULL;
315 }
316
317 slash = strrchr(path, '/');
318 if (asprintf(&links[link_num], "%s/%s", link_path, slash + 1) > 0)
319 link_num++;
320 else
321 links[link_num] = NULL;
322
323 return links;
324
325err:
326 free(links);
327 return NULL;
328}
329
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700330static void handle_device_event(struct uevent *uevent)
331{
332 char devpath[96];
Mike Lockwood93ac1552010-05-06 13:30:09 -0400333 int devpath_ready = 0;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700334 char *base, *name;
Colin Crossb0ab94b2010-04-08 16:16:20 -0700335 char **links = NULL;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700336 int block;
Colin Crossb0ab94b2010-04-08 16:16:20 -0700337 int i;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700338
339 /* if it's not a /dev device, nothing to do */
340 if((uevent->major < 0) || (uevent->minor < 0))
341 return;
342
343 /* do we have a name? */
344 name = strrchr(uevent->path, '/');
345 if(!name)
346 return;
347 name++;
348
349 /* too-long names would overrun our buffer */
350 if(strlen(name) > 64)
351 return;
352
353 /* are we block or char? where should we live? */
The Android Open Source Project35237d12008-12-17 18:08:08 -0800354 if(!strncmp(uevent->subsystem, "block", 5)) {
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700355 block = 1;
356 base = "/dev/block/";
357 mkdir(base, 0755);
Colin Crossb0ab94b2010-04-08 16:16:20 -0700358 if (!strncmp(uevent->path, "/devices/platform/", 18))
359 links = parse_platform_block_device(uevent);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700360 } else {
361 block = 0;
362 /* this should probably be configurable somehow */
Mike Lockwood93ac1552010-05-06 13:30:09 -0400363 if (!strncmp(uevent->subsystem, "usb", 3)) {
364 if (!strcmp(uevent->subsystem, "usb")) {
365 /* This imitates the file system that would be created
366 * if we were using devfs instead.
367 * Minors are broken up into groups of 128, starting at "001"
368 */
369 int bus_id = uevent->minor / 128 + 1;
370 int device_id = uevent->minor % 128 + 1;
371 /* build directories */
372 mkdir("/dev/bus", 0755);
373 mkdir("/dev/bus/usb", 0755);
374 snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d", bus_id);
375 mkdir(devpath, 0755);
376 snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d/%03d", bus_id, device_id);
377 devpath_ready = 1;
378 } else {
379 /* ignore other USB events */
380 return;
381 }
382 } else if (!strncmp(uevent->subsystem, "graphics", 8)) {
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700383 base = "/dev/graphics/";
384 mkdir(base, 0755);
The Android Open Source Project35237d12008-12-17 18:08:08 -0800385 } else if (!strncmp(uevent->subsystem, "oncrpc", 6)) {
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700386 base = "/dev/oncrpc/";
387 mkdir(base, 0755);
The Android Open Source Project35237d12008-12-17 18:08:08 -0800388 } else if (!strncmp(uevent->subsystem, "adsp", 4)) {
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700389 base = "/dev/adsp/";
390 mkdir(base, 0755);
Iliyan Malchevfc0182e2009-05-01 10:25:05 -0700391 } else if (!strncmp(uevent->subsystem, "msm_camera", 10)) {
392 base = "/dev/msm_camera/";
393 mkdir(base, 0755);
394 } else if(!strncmp(uevent->subsystem, "input", 5)) {
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700395 base = "/dev/input/";
396 mkdir(base, 0755);
The Android Open Source Project35237d12008-12-17 18:08:08 -0800397 } else if(!strncmp(uevent->subsystem, "mtd", 3)) {
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700398 base = "/dev/mtd/";
399 mkdir(base, 0755);
Xiaopeng Yang5bb44c82008-11-25 16:20:07 -0800400 } else if(!strncmp(uevent->subsystem, "sound", 5)) {
401 base = "/dev/snd/";
402 mkdir(base, 0755);
The Android Open Source Project35237d12008-12-17 18:08:08 -0800403 } else if(!strncmp(uevent->subsystem, "misc", 4) &&
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700404 !strncmp(name, "log_", 4)) {
405 base = "/dev/log/";
406 mkdir(base, 0755);
407 name += 4;
408 } else
409 base = "/dev/";
410 }
411
Mike Lockwood93ac1552010-05-06 13:30:09 -0400412 if (!devpath_ready)
413 snprintf(devpath, sizeof(devpath), "%s%s", base, name);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700414
415 if(!strcmp(uevent->action, "add")) {
416 make_device(devpath, block, uevent->major, uevent->minor);
Colin Crossb0ab94b2010-04-08 16:16:20 -0700417 if (links) {
418 for (i = 0; links[i]; i++)
419 make_link(devpath, links[i]);
420 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700421 }
422
423 if(!strcmp(uevent->action, "remove")) {
Colin Crossb0ab94b2010-04-08 16:16:20 -0700424 if (links) {
425 for (i = 0; links[i]; i++)
426 remove_link(devpath, links[i]);
427 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700428 unlink(devpath);
Colin Crossb0ab94b2010-04-08 16:16:20 -0700429 }
430
431 if (links) {
432 for (i = 0; links[i]; i++)
433 free(links[i]);
434 free(links);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700435 }
436}
437
438static int load_firmware(int fw_fd, int loading_fd, int data_fd)
439{
440 struct stat st;
441 long len_to_copy;
442 int ret = 0;
443
444 if(fstat(fw_fd, &st) < 0)
445 return -1;
446 len_to_copy = st.st_size;
447
448 write(loading_fd, "1", 1); /* start transfer */
449
450 while (len_to_copy > 0) {
451 char buf[PAGE_SIZE];
452 ssize_t nr;
453
454 nr = read(fw_fd, buf, sizeof(buf));
455 if(!nr)
456 break;
457 if(nr < 0) {
458 ret = -1;
459 break;
460 }
461
462 len_to_copy -= nr;
463 while (nr > 0) {
464 ssize_t nw = 0;
465
466 nw = write(data_fd, buf + nw, nr);
467 if(nw <= 0) {
468 ret = -1;
469 goto out;
470 }
471 nr -= nw;
472 }
473 }
474
475out:
476 if(!ret)
477 write(loading_fd, "0", 1); /* successful end of transfer */
478 else
479 write(loading_fd, "-1", 2); /* abort transfer */
480
481 return ret;
482}
483
484static void process_firmware_event(struct uevent *uevent)
485{
486 char *root, *loading, *data, *file;
487 int l, loading_fd, data_fd, fw_fd;
488
489 log_event_print("firmware event { '%s', '%s' }\n",
490 uevent->path, uevent->firmware);
491
492 l = asprintf(&root, SYSFS_PREFIX"%s/", uevent->path);
493 if (l == -1)
494 return;
495
496 l = asprintf(&loading, "%sloading", root);
497 if (l == -1)
498 goto root_free_out;
499
500 l = asprintf(&data, "%sdata", root);
501 if (l == -1)
502 goto loading_free_out;
503
504 l = asprintf(&file, FIRMWARE_DIR"/%s", uevent->firmware);
505 if (l == -1)
506 goto data_free_out;
507
508 loading_fd = open(loading, O_WRONLY);
509 if(loading_fd < 0)
510 goto file_free_out;
511
512 data_fd = open(data, O_WRONLY);
513 if(data_fd < 0)
514 goto loading_close_out;
515
516 fw_fd = open(file, O_RDONLY);
517 if(fw_fd < 0)
518 goto data_close_out;
519
520 if(!load_firmware(fw_fd, loading_fd, data_fd))
521 log_event_print("firmware copy success { '%s', '%s' }\n", root, file);
522 else
523 log_event_print("firmware copy failure { '%s', '%s' }\n", root, file);
524
525 close(fw_fd);
526data_close_out:
527 close(data_fd);
528loading_close_out:
529 close(loading_fd);
530file_free_out:
531 free(file);
532data_free_out:
533 free(data);
534loading_free_out:
535 free(loading);
536root_free_out:
537 free(root);
538}
539
540static void handle_firmware_event(struct uevent *uevent)
541{
542 pid_t pid;
Colin Cross982a8152010-06-03 12:21:01 -0700543 int status;
544 int ret;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700545
546 if(strcmp(uevent->subsystem, "firmware"))
547 return;
548
549 if(strcmp(uevent->action, "add"))
550 return;
551
552 /* we fork, to avoid making large memory allocations in init proper */
553 pid = fork();
554 if (!pid) {
555 process_firmware_event(uevent);
556 exit(EXIT_SUCCESS);
Colin Cross982a8152010-06-03 12:21:01 -0700557 } else {
558 do {
559 ret = waitpid(pid, &status, 0);
560 } while (ret == -1 && errno == EINTR);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700561 }
562}
563
564#define UEVENT_MSG_LEN 1024
Colin Cross0dd7ca62010-04-13 19:25:51 -0700565void handle_device_fd()
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700566{
Nick Kralevich5f5d5c82010-07-19 14:31:20 -0700567 for(;;) {
568 char msg[UEVENT_MSG_LEN+2];
569 char cred_msg[CMSG_SPACE(sizeof(struct ucred))];
570 struct iovec iov = {msg, sizeof(msg)};
571 struct sockaddr_nl snl;
572 struct msghdr hdr = {&snl, sizeof(snl), &iov, 1, cred_msg, sizeof(cred_msg), 0};
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700573
Nick Kralevichfad72042010-07-19 15:53:05 -0700574 ssize_t n = recvmsg(device_fd, &hdr, 0);
Nick Kralevich5f5d5c82010-07-19 14:31:20 -0700575 if (n <= 0) {
576 break;
577 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700578
Nick Kralevich5f5d5c82010-07-19 14:31:20 -0700579 if ((snl.nl_groups != 1) || (snl.nl_pid != 0)) {
580 /* ignoring non-kernel netlink multicast message */
581 continue;
582 }
583
584 struct cmsghdr * cmsg = CMSG_FIRSTHDR(&hdr);
585 if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
586 /* no sender credentials received, ignore message */
587 continue;
588 }
589
590 struct ucred * cred = (struct ucred *)CMSG_DATA(cmsg);
591 if (cred->uid != 0) {
592 /* message from non-root user, ignore */
593 continue;
594 }
595
596 if(n >= UEVENT_MSG_LEN) /* overflow -- discard */
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700597 continue;
598
599 msg[n] = '\0';
600 msg[n+1] = '\0';
601
Nick Kralevich5f5d5c82010-07-19 14:31:20 -0700602 struct uevent uevent;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700603 parse_event(msg, &uevent);
604
605 handle_device_event(&uevent);
606 handle_firmware_event(&uevent);
607 }
608}
609
610/* Coldboot walks parts of the /sys tree and pokes the uevent files
611** to cause the kernel to regenerate device add events that happened
612** before init's device manager was started
613**
614** We drain any pending events from the netlink socket every time
615** we poke another uevent file to make sure we don't overrun the
616** socket's buffer.
617*/
618
Colin Cross0dd7ca62010-04-13 19:25:51 -0700619static void do_coldboot(DIR *d)
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700620{
621 struct dirent *de;
622 int dfd, fd;
623
624 dfd = dirfd(d);
625
626 fd = openat(dfd, "uevent", O_WRONLY);
627 if(fd >= 0) {
628 write(fd, "add\n", 4);
629 close(fd);
Colin Cross0dd7ca62010-04-13 19:25:51 -0700630 handle_device_fd();
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700631 }
632
633 while((de = readdir(d))) {
634 DIR *d2;
635
636 if(de->d_type != DT_DIR || de->d_name[0] == '.')
637 continue;
638
639 fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
640 if(fd < 0)
641 continue;
642
643 d2 = fdopendir(fd);
644 if(d2 == 0)
645 close(fd);
646 else {
Colin Cross0dd7ca62010-04-13 19:25:51 -0700647 do_coldboot(d2);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700648 closedir(d2);
649 }
650 }
651}
652
Colin Cross0dd7ca62010-04-13 19:25:51 -0700653static void coldboot(const char *path)
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700654{
655 DIR *d = opendir(path);
656 if(d) {
Colin Cross0dd7ca62010-04-13 19:25:51 -0700657 do_coldboot(d);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700658 closedir(d);
659 }
660}
661
Colin Cross0dd7ca62010-04-13 19:25:51 -0700662void device_init(void)
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700663{
664 suseconds_t t0, t1;
Colin Crossf83d0b92010-04-21 12:04:20 -0700665 struct stat info;
666 int fd;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700667
Colin Cross0dd7ca62010-04-13 19:25:51 -0700668 device_fd = open_uevent_socket();
669 if(device_fd < 0)
670 return;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700671
Colin Cross0dd7ca62010-04-13 19:25:51 -0700672 fcntl(device_fd, F_SETFD, FD_CLOEXEC);
673 fcntl(device_fd, F_SETFL, O_NONBLOCK);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700674
Colin Crossf83d0b92010-04-21 12:04:20 -0700675 if (stat(coldboot_done, &info) < 0) {
676 t0 = get_usecs();
677 coldboot("/sys/class");
678 coldboot("/sys/block");
679 coldboot("/sys/devices");
680 t1 = get_usecs();
681 fd = open(coldboot_done, O_WRONLY|O_CREAT, 0000);
682 close(fd);
683 log_event_print("coldboot %ld uS\n", ((long) (t1 - t0)));
684 } else {
685 log_event_print("skipping coldboot, already done\n");
686 }
Colin Cross0dd7ca62010-04-13 19:25:51 -0700687}
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700688
Colin Cross0dd7ca62010-04-13 19:25:51 -0700689int get_device_fd()
690{
691 return device_fd;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700692}