1 /* 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 <stddef.h> 19 #include <stdio.h> 20 #include <stdlib.h> 21 #include <sys/stat.h> 22 #include <sys/types.h> 23 24 #include <fcntl.h> 25 #include <dirent.h> 26 #include <unistd.h> 27 #include <string.h> 28 29 #include <sys/socket.h> 30 #include <sys/un.h> 31 #include <linux/netlink.h> 32 #include <private/android_filesystem_config.h> 33 #include <sys/time.h> 34 #include <asm/page.h> 35 #include <sys/wait.h> 36 37 #include <cutils/list.h> 38 #include <cutils/uevent.h> 39 40 #include "devices.h" 41 #include "util.h" 42 #include "log.h" 43 44 #define SYSFS_PREFIX "/sys" 45 #define FIRMWARE_DIR1 "/etc/firmware" 46 #define FIRMWARE_DIR2 "/vendor/firmware" 47 48 static int device_fd = -1; 49 50 struct uevent { 51 const char *action; 52 const char *path; 53 const char *subsystem; 54 const char *firmware; 55 const char *partition_name; 56 int partition_num; 57 int major; 58 int minor; 59 }; 60 61 struct perms_ { 62 char *name; 63 char *attr; 64 mode_t perm; 65 unsigned int uid; 66 unsigned int gid; 67 unsigned short prefix; 68 }; 69 70 struct perm_node { 71 struct perms_ dp; 72 struct listnode plist; 73 }; 74 75 struct platform_node { 76 char *name; 77 int name_len; 78 struct listnode list; 79 }; 80 81 static list_declare(sys_perms); 82 static list_declare(dev_perms); 83 static list_declare(platform_names); 84 85 int add_dev_perms(const char *name, const char *attr, 86 mode_t perm, unsigned int uid, unsigned int gid, 87 unsigned short prefix) { 88 struct perm_node *node = calloc(1, sizeof(*node)); 89 if (!node) 90 return -ENOMEM; 91 92 node->dp.name = strdup(name); 93 if (!node->dp.name) 94 return -ENOMEM; 95 96 if (attr) { 97 node->dp.attr = strdup(attr); 98 if (!node->dp.attr) 99 return -ENOMEM; 100 } 101 102 node->dp.perm = perm; 103 node->dp.uid = uid; 104 node->dp.gid = gid; 105 node->dp.prefix = prefix; 106 107 if (attr) 108 list_add_tail(&sys_perms, &node->plist); 109 else 110 list_add_tail(&dev_perms, &node->plist); 111 112 return 0; 113 } 114 115 void fixup_sys_perms(const char *upath) 116 { 117 char buf[512]; 118 struct listnode *node; 119 struct perms_ *dp; 120 121 /* upaths omit the "/sys" that paths in this list 122 * contain, so we add 4 when comparing... 123 */ 124 list_for_each(node, &sys_perms) { 125 dp = &(node_to_item(node, struct perm_node, plist))->dp; 126 if (dp->prefix) { 127 if (strncmp(upath, dp->name + 4, strlen(dp->name + 4))) 128 continue; 129 } else { 130 if (strcmp(upath, dp->name + 4)) 131 continue; 132 } 133 134 if ((strlen(upath) + strlen(dp->attr) + 6) > sizeof(buf)) 135 return; 136 137 sprintf(buf,"/sys%s/%s", upath, dp->attr); 138 INFO("fixup %s %d %d 0%o\n", buf, dp->uid, dp->gid, dp->perm); 139 chown(buf, dp->uid, dp->gid); 140 chmod(buf, dp->perm); 141 } 142 } 143 144 static mode_t get_device_perm(const char *path, unsigned *uid, unsigned *gid) 145 { 146 mode_t perm; 147 struct listnode *node; 148 struct perm_node *perm_node; 149 struct perms_ *dp; 150 151 /* search the perms list in reverse so that ueventd.$hardware can 152 * override ueventd.rc 153 */ 154 list_for_each_reverse(node, &dev_perms) { 155 perm_node = node_to_item(node, struct perm_node, plist); 156 dp = &perm_node->dp; 157 158 if (dp->prefix) { 159 if (strncmp(path, dp->name, strlen(dp->name))) 160 continue; 161 } else { 162 if (strcmp(path, dp->name)) 163 continue; 164 } 165 *uid = dp->uid; 166 *gid = dp->gid; 167 return dp->perm; 168 } 169 /* Default if nothing found. */ 170 *uid = 0; 171 *gid = 0; 172 return 0600; 173 } 174 175 static void make_device(const char *path, 176 const char *upath, 177 int block, int major, int minor) 178 { 179 unsigned uid; 180 unsigned gid; 181 mode_t mode; 182 dev_t dev; 183 184 mode = get_device_perm(path, &uid, &gid) | (block ? S_IFBLK : S_IFCHR); 185 dev = makedev(major, minor); 186 /* Temporarily change egid to avoid race condition setting the gid of the 187 * device node. Unforunately changing the euid would prevent creation of 188 * some device nodes, so the uid has to be set with chown() and is still 189 * racy. Fixing the gid race at least fixed the issue with system_server 190 * opening dynamic input devices under the AID_INPUT gid. */ 191 setegid(gid); 192 mknod(path, mode, dev); 193 chown(path, uid, -1); 194 setegid(AID_ROOT); 195 } 196 197 static void add_platform_device(const char *name) 198 { 199 int name_len = strlen(name); 200 struct listnode *node; 201 struct platform_node *bus; 202 203 list_for_each_reverse(node, &platform_names) { 204 bus = node_to_item(node, struct platform_node, list); 205 if ((bus->name_len < name_len) && 206 (name[bus->name_len] == '/') && 207 !strncmp(name, bus->name, bus->name_len)) 208 /* subdevice of an existing platform, ignore it */ 209 return; 210 } 211 212 INFO("adding platform device %s\n", name); 213 214 bus = calloc(1, sizeof(struct platform_node)); 215 bus->name = strdup(name); 216 bus->name_len = name_len; 217 list_add_tail(&platform_names, &bus->list); 218 } 219 220 /* 221 * given a name that may start with a platform device, find the length of the 222 * platform device prefix. If it doesn't start with a platform device, return 223 * 0. 224 */ 225 static const char *find_platform_device(const char *name) 226 { 227 int name_len = strlen(name); 228 struct listnode *node; 229 struct platform_node *bus; 230 231 list_for_each_reverse(node, &platform_names) { 232 bus = node_to_item(node, struct platform_node, list); 233 if ((bus->name_len < name_len) && 234 (name[bus->name_len] == '/') && 235 !strncmp(name, bus->name, bus->name_len)) 236 return bus->name; 237 } 238 239 return NULL; 240 } 241 242 static void remove_platform_device(const char *name) 243 { 244 struct listnode *node; 245 struct platform_node *bus; 246 247 list_for_each_reverse(node, &platform_names) { 248 bus = node_to_item(node, struct platform_node, list); 249 if (!strcmp(name, bus->name)) { 250 INFO("removing platform device %s\n", name); 251 free(bus->name); 252 list_remove(node); 253 free(bus); 254 return; 255 } 256 } 257 } 258 259 #if LOG_UEVENTS 260 261 static inline suseconds_t get_usecs(void) 262 { 263 struct timeval tv; 264 gettimeofday(&tv, 0); 265 return tv.tv_sec * (suseconds_t) 1000000 + tv.tv_usec; 266 } 267 268 #define log_event_print(x...) INFO(x) 269 270 #else 271 272 #define log_event_print(fmt, args...) do { } while (0) 273 #define get_usecs() 0 274 275 #endif 276 277 static void parse_event(const char *msg, struct uevent *uevent) 278 { 279 uevent->action = ""; 280 uevent->path = ""; 281 uevent->subsystem = ""; 282 uevent->firmware = ""; 283 uevent->major = -1; 284 uevent->minor = -1; 285 uevent->partition_name = NULL; 286 uevent->partition_num = -1; 287 288 /* currently ignoring SEQNUM */ 289 while(*msg) { 290 if(!strncmp(msg, "ACTION=", 7)) { 291 msg += 7; 292 uevent->action = msg; 293 } else if(!strncmp(msg, "DEVPATH=", 8)) { 294 msg += 8; 295 uevent->path = msg; 296 } else if(!strncmp(msg, "SUBSYSTEM=", 10)) { 297 msg += 10; 298 uevent->subsystem = msg; 299 } else if(!strncmp(msg, "FIRMWARE=", 9)) { 300 msg += 9; 301 uevent->firmware = msg; 302 } else if(!strncmp(msg, "MAJOR=", 6)) { 303 msg += 6; 304 uevent->major = atoi(msg); 305 } else if(!strncmp(msg, "MINOR=", 6)) { 306 msg += 6; 307 uevent->minor = atoi(msg); 308 } else if(!strncmp(msg, "PARTN=", 6)) { 309 msg += 6; 310 uevent->partition_num = atoi(msg); 311 } else if(!strncmp(msg, "PARTNAME=", 9)) { 312 msg += 9; 313 uevent->partition_name = msg; 314 } 315 316 /* advance to after the next \0 */ 317 while(*msg++) 318 ; 319 } 320 321 log_event_print("event { '%s', '%s', '%s', '%s', %d, %d }\n", 322 uevent->action, uevent->path, uevent->subsystem, 323 uevent->firmware, uevent->major, uevent->minor); 324 } 325 326 static char **get_character_device_symlinks(struct uevent *uevent) 327 { 328 const char *parent; 329 char *slash; 330 char **links; 331 int link_num = 0; 332 int width; 333 334 if (strncmp(uevent->path, "/devices/platform/", 18)) 335 return NULL; 336 337 links = malloc(sizeof(char *) * 2); 338 if (!links) 339 return NULL; 340 memset(links, 0, sizeof(char *) * 2); 341 342 /* skip "/devices/platform/<driver>" */ 343 parent = strchr(uevent->path + 18, '/'); 344 if (!*parent) 345 goto err; 346 347 if (!strncmp(parent, "/usb", 4)) { 348 /* skip root hub name and device. use device interface */ 349 while (*++parent && *parent != '/'); 350 if (*parent) 351 while (*++parent && *parent != '/'); 352 if (!*parent) 353 goto err; 354 slash = strchr(++parent, '/'); 355 if (!slash) 356 goto err; 357 width = slash - parent; 358 if (width <= 0) 359 goto err; 360 361 if (asprintf(&links[link_num], "/dev/usb/%s%.*s", uevent->subsystem, width, parent) > 0) 362 link_num++; 363 else 364 links[link_num] = NULL; 365 mkdir("/dev/usb", 0755); 366 } 367 else { 368 goto err; 369 } 370 371 return links; 372 err: 373 free(links); 374 return NULL; 375 } 376 377 static char **parse_platform_block_device(struct uevent *uevent) 378 { 379 const char *device; 380 const char *path; 381 char *slash; 382 int width; 383 char buf[256]; 384 char link_path[256]; 385 int fd; 386 int link_num = 0; 387 int ret; 388 char *p; 389 unsigned int size; 390 struct stat info; 391 392 char **links = malloc(sizeof(char *) * 4); 393 if (!links) 394 return NULL; 395 memset(links, 0, sizeof(char *) * 4); 396 397 /* Drop "/devices/platform/" */ 398 path = uevent->path; 399 device = path + 18; 400 device = find_platform_device(device); 401 if (!device) 402 goto err; 403 404 INFO("found platform device %s\n", device); 405 406 snprintf(link_path, sizeof(link_path), "/dev/block/platform/%s", device); 407 408 if (uevent->partition_name) { 409 p = strdup(uevent->partition_name); 410 sanitize(p); 411 if (asprintf(&links[link_num], "%s/by-name/%s", link_path, p) > 0) 412 link_num++; 413 else 414 links[link_num] = NULL; 415 free(p); 416 } 417 418 if (uevent->partition_num >= 0) { 419 if (asprintf(&links[link_num], "%s/by-num/p%d", link_path, uevent->partition_num) > 0) 420 link_num++; 421 else 422 links[link_num] = NULL; 423 } 424 425 slash = strrchr(path, '/'); 426 if (asprintf(&links[link_num], "%s/%s", link_path, slash + 1) > 0) 427 link_num++; 428 else 429 links[link_num] = NULL; 430 431 return links; 432 433 err: 434 free(links); 435 return NULL; 436 } 437 438 static void handle_device(const char *action, const char *devpath, 439 const char *path, int block, int major, int minor, char **links) 440 { 441 int i; 442 443 if(!strcmp(action, "add")) { 444 make_device(devpath, path, block, major, minor); 445 if (links) { 446 for (i = 0; links[i]; i++) 447 make_link(devpath, links[i]); 448 } 449 } 450 451 if(!strcmp(action, "remove")) { 452 if (links) { 453 for (i = 0; links[i]; i++) 454 remove_link(devpath, links[i]); 455 } 456 unlink(devpath); 457 } 458 459 if (links) { 460 for (i = 0; links[i]; i++) 461 free(links[i]); 462 free(links); 463 } 464 } 465 466 static void handle_platform_device_event(struct uevent *uevent) 467 { 468 const char *name = uevent->path + 18; /* length of /devices/platform/ */ 469 470 if (!strcmp(uevent->action, "add")) 471 add_platform_device(name); 472 else if (!strcmp(uevent->action, "remove")) 473 remove_platform_device(name); 474 } 475 476 static const char *parse_device_name(struct uevent *uevent, unsigned int len) 477 { 478 const char *name; 479 480 /* if it's not a /dev device, nothing else to do */ 481 if((uevent->major < 0) || (uevent->minor < 0)) 482 return NULL; 483 484 /* do we have a name? */ 485 name = strrchr(uevent->path, '/'); 486 if(!name) 487 return NULL; 488 name++; 489 490 /* too-long names would overrun our buffer */ 491 if(strlen(name) > len) 492 return NULL; 493 494 return name; 495 } 496 497 static void handle_block_device_event(struct uevent *uevent) 498 { 499 const char *base = "/dev/block/"; 500 const char *name; 501 char devpath[96]; 502 char **links = NULL; 503 504 name = parse_device_name(uevent, 64); 505 if (!name) 506 return; 507 508 snprintf(devpath, sizeof(devpath), "%s%s", base, name); 509 mkdir(base, 0755); 510 511 if (!strncmp(uevent->path, "/devices/platform/", 18)) 512 links = parse_platform_block_device(uevent); 513 514 handle_device(uevent->action, devpath, uevent->path, 1, 515 uevent->major, uevent->minor, links); 516 } 517 518 static void handle_generic_device_event(struct uevent *uevent) 519 { 520 char *base; 521 const char *name; 522 char devpath[96] = {0}; 523 char **links = NULL; 524 525 name = parse_device_name(uevent, 64); 526 if (!name) 527 return; 528 529 if (!strncmp(uevent->subsystem, "usb", 3)) { 530 if (!strcmp(uevent->subsystem, "usb")) { 531 /* This imitates the file system that would be created 532 * if we were using devfs instead. 533 * Minors are broken up into groups of 128, starting at "001" 534 */ 535 int bus_id = uevent->minor / 128 + 1; 536 int device_id = uevent->minor % 128 + 1; 537 /* build directories */ 538 mkdir("/dev/bus", 0755); 539 mkdir("/dev/bus/usb", 0755); 540 snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d", bus_id); 541 mkdir(devpath, 0755); 542 snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d/%03d", bus_id, device_id); 543 } else { 544 /* ignore other USB events */ 545 return; 546 } 547 } else if (!strncmp(uevent->subsystem, "graphics", 8)) { 548 base = "/dev/graphics/"; 549 mkdir(base, 0755); 550 } else if (!strncmp(uevent->subsystem, "oncrpc", 6)) { 551 base = "/dev/oncrpc/"; 552 mkdir(base, 0755); 553 } else if (!strncmp(uevent->subsystem, "adsp", 4)) { 554 base = "/dev/adsp/"; 555 mkdir(base, 0755); 556 } else if (!strncmp(uevent->subsystem, "msm_camera", 10)) { 557 base = "/dev/msm_camera/"; 558 mkdir(base, 0755); 559 } else if(!strncmp(uevent->subsystem, "input", 5)) { 560 base = "/dev/input/"; 561 mkdir(base, 0755); 562 } else if(!strncmp(uevent->subsystem, "mtd", 3)) { 563 base = "/dev/mtd/"; 564 mkdir(base, 0755); 565 } else if(!strncmp(uevent->subsystem, "sound", 5)) { 566 base = "/dev/snd/"; 567 mkdir(base, 0755); 568 } else if(!strncmp(uevent->subsystem, "misc", 4) && 569 !strncmp(name, "log_", 4)) { 570 base = "/dev/log/"; 571 mkdir(base, 0755); 572 name += 4; 573 } else 574 base = "/dev/"; 575 links = get_character_device_symlinks(uevent); 576 577 if (!devpath[0]) 578 snprintf(devpath, sizeof(devpath), "%s%s", base, name); 579 580 handle_device(uevent->action, devpath, uevent->path, 0, 581 uevent->major, uevent->minor, links); 582 } 583 584 static void handle_device_event(struct uevent *uevent) 585 { 586 if (!strcmp(uevent->action,"add")) 587 fixup_sys_perms(uevent->path); 588 589 if (!strncmp(uevent->subsystem, "block", 5)) { 590 handle_block_device_event(uevent); 591 } else if (!strncmp(uevent->subsystem, "platform", 8)) { 592 handle_platform_device_event(uevent); 593 } else { 594 handle_generic_device_event(uevent); 595 } 596 } 597 598 static int load_firmware(int fw_fd, int loading_fd, int data_fd) 599 { 600 struct stat st; 601 long len_to_copy; 602 int ret = 0; 603 604 if(fstat(fw_fd, &st) < 0) 605 return -1; 606 len_to_copy = st.st_size; 607 608 write(loading_fd, "1", 1); /* start transfer */ 609 610 while (len_to_copy > 0) { 611 char buf[PAGE_SIZE]; 612 ssize_t nr; 613 614 nr = read(fw_fd, buf, sizeof(buf)); 615 if(!nr) 616 break; 617 if(nr < 0) { 618 ret = -1; 619 break; 620 } 621 622 len_to_copy -= nr; 623 while (nr > 0) { 624 ssize_t nw = 0; 625 626 nw = write(data_fd, buf + nw, nr); 627 if(nw <= 0) { 628 ret = -1; 629 goto out; 630 } 631 nr -= nw; 632 } 633 } 634 635 out: 636 if(!ret) 637 write(loading_fd, "0", 1); /* successful end of transfer */ 638 else 639 write(loading_fd, "-1", 2); /* abort transfer */ 640 641 return ret; 642 } 643 644 static int is_booting(void) 645 { 646 return access("/dev/.booting", F_OK) == 0; 647 } 648 649 static void process_firmware_event(struct uevent *uevent) 650 { 651 char *root, *loading, *data, *file1 = NULL, *file2 = NULL; 652 int l, loading_fd, data_fd, fw_fd; 653 int booting = is_booting(); 654 655 INFO("firmware: loading '%s' for '%s'\n", 656 uevent->firmware, uevent->path); 657 658 l = asprintf(&root, SYSFS_PREFIX"%s/", uevent->path); 659 if (l == -1) 660 return; 661 662 l = asprintf(&loading, "%sloading", root); 663 if (l == -1) 664 goto root_free_out; 665 666 l = asprintf(&data, "%sdata", root); 667 if (l == -1) 668 goto loading_free_out; 669 670 l = asprintf(&file1, FIRMWARE_DIR1"/%s", uevent->firmware); 671 if (l == -1) 672 goto data_free_out; 673 674 l = asprintf(&file2, FIRMWARE_DIR2"/%s", uevent->firmware); 675 if (l == -1) 676 goto data_free_out; 677 678 loading_fd = open(loading, O_WRONLY); 679 if(loading_fd < 0) 680 goto file_free_out; 681 682 data_fd = open(data, O_WRONLY); 683 if(data_fd < 0) 684 goto loading_close_out; 685 686 try_loading_again: 687 fw_fd = open(file1, O_RDONLY); 688 if(fw_fd < 0) { 689 fw_fd = open(file2, O_RDONLY); 690 if (fw_fd < 0) { 691 if (booting) { 692 /* If we're not fully booted, we may be missing 693 * filesystems needed for firmware, wait and retry. 694 */ 695 usleep(100000); 696 booting = is_booting(); 697 goto try_loading_again; 698 } 699 INFO("firmware: could not open '%s' %d\n", uevent->firmware, errno); 700 write(loading_fd, "-1", 2); 701 goto data_close_out; 702 } 703 } 704 705 if(!load_firmware(fw_fd, loading_fd, data_fd)) 706 INFO("firmware: copy success { '%s', '%s' }\n", root, uevent->firmware); 707 else 708 INFO("firmware: copy failure { '%s', '%s' }\n", root, uevent->firmware); 709 710 close(fw_fd); 711 data_close_out: 712 close(data_fd); 713 loading_close_out: 714 close(loading_fd); 715 file_free_out: 716 free(file1); 717 free(file2); 718 data_free_out: 719 free(data); 720 loading_free_out: 721 free(loading); 722 root_free_out: 723 free(root); 724 } 725 726 static void handle_firmware_event(struct uevent *uevent) 727 { 728 pid_t pid; 729 int ret; 730 731 if(strcmp(uevent->subsystem, "firmware")) 732 return; 733 734 if(strcmp(uevent->action, "add")) 735 return; 736 737 /* we fork, to avoid making large memory allocations in init proper */ 738 pid = fork(); 739 if (!pid) { 740 process_firmware_event(uevent); 741 exit(EXIT_SUCCESS); 742 } 743 } 744 745 #define UEVENT_MSG_LEN 1024 746 void handle_device_fd() 747 { 748 char msg[UEVENT_MSG_LEN+2]; 749 int n; 750 while ((n = uevent_kernel_multicast_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) { 751 if(n >= UEVENT_MSG_LEN) /* overflow -- discard */ 752 continue; 753 754 msg[n] = '\0'; 755 msg[n+1] = '\0'; 756 757 struct uevent uevent; 758 parse_event(msg, &uevent); 759 760 handle_device_event(&uevent); 761 handle_firmware_event(&uevent); 762 } 763 } 764 765 /* Coldboot walks parts of the /sys tree and pokes the uevent files 766 ** to cause the kernel to regenerate device add events that happened 767 ** before init's device manager was started 768 ** 769 ** We drain any pending events from the netlink socket every time 770 ** we poke another uevent file to make sure we don't overrun the 771 ** socket's buffer. 772 */ 773 774 static void do_coldboot(DIR *d) 775 { 776 struct dirent *de; 777 int dfd, fd; 778 779 dfd = dirfd(d); 780 781 fd = openat(dfd, "uevent", O_WRONLY); 782 if(fd >= 0) { 783 write(fd, "add\n", 4); 784 close(fd); 785 handle_device_fd(); 786 } 787 788 while((de = readdir(d))) { 789 DIR *d2; 790 791 if(de->d_type != DT_DIR || de->d_name[0] == '.') 792 continue; 793 794 fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY); 795 if(fd < 0) 796 continue; 797 798 d2 = fdopendir(fd); 799 if(d2 == 0) 800 close(fd); 801 else { 802 do_coldboot(d2); 803 closedir(d2); 804 } 805 } 806 } 807 808 static void coldboot(const char *path) 809 { 810 DIR *d = opendir(path); 811 if(d) { 812 do_coldboot(d); 813 closedir(d); 814 } 815 } 816 817 void device_init(void) 818 { 819 suseconds_t t0, t1; 820 struct stat info; 821 int fd; 822 823 /* is 64K enough? udev uses 16MB! */ 824 device_fd = uevent_open_socket(64*1024, true); 825 if(device_fd < 0) 826 return; 827 828 fcntl(device_fd, F_SETFD, FD_CLOEXEC); 829 fcntl(device_fd, F_SETFL, O_NONBLOCK); 830 831 if (stat(coldboot_done, &info) < 0) { 832 t0 = get_usecs(); 833 coldboot("/sys/class"); 834 coldboot("/sys/block"); 835 coldboot("/sys/devices"); 836 t1 = get_usecs(); 837 fd = open(coldboot_done, O_WRONLY|O_CREAT, 0000); 838 close(fd); 839 log_event_print("coldboot %ld uS\n", ((long) (t1 - t0))); 840 } else { 841 log_event_print("skipping coldboot, already done\n"); 842 } 843 } 844 845 int get_device_fd() 846 { 847 return device_fd; 848 } 849