diff options
Diffstat (limited to 'init/devices.c')
-rw-r--r-- | init/devices.c | 342 |
1 files changed, 234 insertions, 108 deletions
diff --git a/init/devices.c b/init/devices.c index 9c07e99..60659ce 100644 --- a/init/devices.c +++ b/init/devices.c @@ -99,8 +99,15 @@ struct perm_node { struct listnode plist; }; +struct platform_node { + char *name; + int name_len; + struct listnode list; +}; + static list_declare(sys_perms); static list_declare(dev_perms); +static list_declare(platform_names); int add_dev_perms(const char *name, const char *attr, mode_t perm, unsigned int uid, unsigned int gid, @@ -214,6 +221,68 @@ static void make_device(const char *path, setegid(AID_ROOT); } +static void add_platform_device(const char *name) +{ + int name_len = strlen(name); + struct listnode *node; + struct platform_node *bus; + + list_for_each_reverse(node, &platform_names) { + bus = node_to_item(node, struct platform_node, list); + if ((bus->name_len < name_len) && + (name[bus->name_len] == '/') && + !strncmp(name, bus->name, bus->name_len)) + /* subdevice of an existing platform, ignore it */ + return; + } + + INFO("adding platform device %s\n", name); + + bus = calloc(1, sizeof(struct platform_node)); + bus->name = strdup(name); + bus->name_len = name_len; + list_add_tail(&platform_names, &bus->list); +} + +/* + * given a name that may start with a platform device, find the length of the + * platform device prefix. If it doesn't start with a platform device, return + * 0. + */ +static const char *find_platform_device(const char *name) +{ + int name_len = strlen(name); + struct listnode *node; + struct platform_node *bus; + + list_for_each_reverse(node, &platform_names) { + bus = node_to_item(node, struct platform_node, list); + if ((bus->name_len < name_len) && + (name[bus->name_len] == '/') && + !strncmp(name, bus->name, bus->name_len)) + return bus->name; + } + + return NULL; +} + +static void remove_platform_device(const char *name) +{ + struct listnode *node; + struct platform_node *bus; + + list_for_each_reverse(node, &platform_names) { + bus = node_to_item(node, struct platform_node, list); + if (!strcmp(name, bus->name)) { + INFO("removing platform device %s\n", name); + free(bus->name); + list_remove(node); + free(bus); + return; + } + } +} + #if LOG_UEVENTS static inline suseconds_t get_usecs(void) @@ -334,7 +403,7 @@ err: static char **parse_platform_block_device(struct uevent *uevent) { - const char *driver; + const char *device; const char *path; char *slash; int width; @@ -354,16 +423,14 @@ static char **parse_platform_block_device(struct uevent *uevent) /* Drop "/devices/platform/" */ path = uevent->path; - driver = path + 18; - slash = strchr(driver, '/'); - if (!slash) - goto err; - width = slash - driver; - if (width <= 0) + device = path + 18; + device = find_platform_device(device); + if (!device) goto err; - snprintf(link_path, sizeof(link_path), "/dev/block/platform/%.*s", - width, driver); + INFO("found platform device %s\n", device); + + snprintf(link_path, sizeof(link_path), "/dev/block/platform/%s", device); if (uevent->partition_name) { p = strdup(uevent->partition_name); @@ -395,104 +462,20 @@ err: return NULL; } -static void handle_device_event(struct uevent *uevent) +static void handle_device(const char *action, const char *devpath, + const char *path, int block, int major, int minor, char **links) { - char devpath[96]; - int devpath_ready = 0; - char *base, *name; - char **links = NULL; - int block; int i; - if (!strcmp(uevent->action,"add")) - fixup_sys_perms(uevent->path); - - /* if it's not a /dev device, nothing else to do */ - if((uevent->major < 0) || (uevent->minor < 0)) - return; - - /* do we have a name? */ - name = strrchr(uevent->path, '/'); - if(!name) - return; - name++; - - /* too-long names would overrun our buffer */ - if(strlen(name) > 64) - return; - - /* are we block or char? where should we live? */ - if(!strncmp(uevent->subsystem, "block", 5)) { - block = 1; - base = "/dev/block/"; - mkdir(base, 0755); - if (!strncmp(uevent->path, "/devices/platform/", 18)) - links = parse_platform_block_device(uevent); - } else { - block = 0; - /* this should probably be configurable somehow */ - if (!strncmp(uevent->subsystem, "usb", 3)) { - if (!strcmp(uevent->subsystem, "usb")) { - /* This imitates the file system that would be created - * if we were using devfs instead. - * Minors are broken up into groups of 128, starting at "001" - */ - int bus_id = uevent->minor / 128 + 1; - int device_id = uevent->minor % 128 + 1; - /* build directories */ - mkdir("/dev/bus", 0755); - mkdir("/dev/bus/usb", 0755); - snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d", bus_id); - mkdir(devpath, 0755); - snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d/%03d", bus_id, device_id); - devpath_ready = 1; - } else { - /* ignore other USB events */ - return; - } - } else if (!strncmp(uevent->subsystem, "graphics", 8)) { - base = "/dev/graphics/"; - mkdir(base, 0755); - } else if (!strncmp(uevent->subsystem, "oncrpc", 6)) { - base = "/dev/oncrpc/"; - mkdir(base, 0755); - } else if (!strncmp(uevent->subsystem, "adsp", 4)) { - base = "/dev/adsp/"; - mkdir(base, 0755); - } else if (!strncmp(uevent->subsystem, "msm_camera", 10)) { - base = "/dev/msm_camera/"; - mkdir(base, 0755); - } else if(!strncmp(uevent->subsystem, "input", 5)) { - base = "/dev/input/"; - mkdir(base, 0755); - } else if(!strncmp(uevent->subsystem, "mtd", 3)) { - base = "/dev/mtd/"; - mkdir(base, 0755); - } else if(!strncmp(uevent->subsystem, "sound", 5)) { - base = "/dev/snd/"; - mkdir(base, 0755); - } else if(!strncmp(uevent->subsystem, "misc", 4) && - !strncmp(name, "log_", 4)) { - base = "/dev/log/"; - mkdir(base, 0755); - name += 4; - } else - base = "/dev/"; - links = get_character_device_symlinks(uevent); - } - - if (!devpath_ready) - snprintf(devpath, sizeof(devpath), "%s%s", base, name); - - if(!strcmp(uevent->action, "add")) { - make_device(devpath, uevent->path, block, uevent->major, uevent->minor); + if(!strcmp(action, "add")) { + make_device(devpath, path, block, major, minor); if (links) { for (i = 0; links[i]; i++) make_link(devpath, links[i]); } } - if(!strcmp(uevent->action, "remove")) { + if(!strcmp(action, "remove")) { if (links) { for (i = 0; links[i]; i++) remove_link(devpath, links[i]); @@ -507,6 +490,138 @@ static void handle_device_event(struct uevent *uevent) } } +static void handle_platform_device_event(struct uevent *uevent) +{ + const char *name = uevent->path + 18; /* length of /devices/platform/ */ + + if (!strcmp(uevent->action, "add")) + add_platform_device(name); + else if (!strcmp(uevent->action, "remove")) + remove_platform_device(name); +} + +static const char *parse_device_name(struct uevent *uevent, unsigned int len) +{ + const char *name; + + /* if it's not a /dev device, nothing else to do */ + if((uevent->major < 0) || (uevent->minor < 0)) + return NULL; + + /* do we have a name? */ + name = strrchr(uevent->path, '/'); + if(!name) + return NULL; + name++; + + /* too-long names would overrun our buffer */ + if(strlen(name) > len) + return NULL; + + return name; +} + +static void handle_block_device_event(struct uevent *uevent) +{ + const char *base = "/dev/block/"; + const char *name; + char devpath[96]; + char **links = NULL; + + name = parse_device_name(uevent, 64); + if (!name) + return; + + snprintf(devpath, sizeof(devpath), "%s%s", base, name); + mkdir(base, 0755); + + if (!strncmp(uevent->path, "/devices/platform/", 18)) + links = parse_platform_block_device(uevent); + + handle_device(uevent->action, devpath, uevent->path, 1, + uevent->major, uevent->minor, links); +} + +static void handle_generic_device_event(struct uevent *uevent) +{ + char *base; + const char *name; + char devpath[96] = {0}; + char **links = NULL; + + name = parse_device_name(uevent, 64); + if (!name) + return; + + if (!strncmp(uevent->subsystem, "usb", 3)) { + if (!strcmp(uevent->subsystem, "usb")) { + /* This imitates the file system that would be created + * if we were using devfs instead. + * Minors are broken up into groups of 128, starting at "001" + */ + int bus_id = uevent->minor / 128 + 1; + int device_id = uevent->minor % 128 + 1; + /* build directories */ + mkdir("/dev/bus", 0755); + mkdir("/dev/bus/usb", 0755); + snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d", bus_id); + mkdir(devpath, 0755); + snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d/%03d", bus_id, device_id); + } else { + /* ignore other USB events */ + return; + } + } else if (!strncmp(uevent->subsystem, "graphics", 8)) { + base = "/dev/graphics/"; + mkdir(base, 0755); + } else if (!strncmp(uevent->subsystem, "oncrpc", 6)) { + base = "/dev/oncrpc/"; + mkdir(base, 0755); + } else if (!strncmp(uevent->subsystem, "adsp", 4)) { + base = "/dev/adsp/"; + mkdir(base, 0755); + } else if (!strncmp(uevent->subsystem, "msm_camera", 10)) { + base = "/dev/msm_camera/"; + mkdir(base, 0755); + } else if(!strncmp(uevent->subsystem, "input", 5)) { + base = "/dev/input/"; + mkdir(base, 0755); + } else if(!strncmp(uevent->subsystem, "mtd", 3)) { + base = "/dev/mtd/"; + mkdir(base, 0755); + } else if(!strncmp(uevent->subsystem, "sound", 5)) { + base = "/dev/snd/"; + mkdir(base, 0755); + } else if(!strncmp(uevent->subsystem, "misc", 4) && + !strncmp(name, "log_", 4)) { + base = "/dev/log/"; + mkdir(base, 0755); + name += 4; + } else + base = "/dev/"; + links = get_character_device_symlinks(uevent); + + if (!devpath[0]) + snprintf(devpath, sizeof(devpath), "%s%s", base, name); + + handle_device(uevent->action, devpath, uevent->path, 0, + uevent->major, uevent->minor, links); +} + +static void handle_device_event(struct uevent *uevent) +{ + if (!strcmp(uevent->action,"add")) + fixup_sys_perms(uevent->path); + + if (!strncmp(uevent->subsystem, "block", 5)) { + handle_block_device_event(uevent); + } else if (!strncmp(uevent->subsystem, "platform", 8)) { + handle_platform_device_event(uevent); + } else { + handle_generic_device_event(uevent); + } +} + static int load_firmware(int fw_fd, int loading_fd, int data_fd) { struct stat st; @@ -553,13 +668,19 @@ out: return ret; } +static int is_booting(void) +{ + return access("/dev/.booting", F_OK) == 0; +} + static void process_firmware_event(struct uevent *uevent) { char *root, *loading, *data, *file1 = NULL, *file2 = NULL; int l, loading_fd, data_fd, fw_fd; + int booting = is_booting(); - log_event_print("firmware event { '%s', '%s' }\n", - uevent->path, uevent->firmware); + INFO("firmware: loading '%s' for '%s'\n", + uevent->firmware, uevent->path); l = asprintf(&root, SYSFS_PREFIX"%s/", uevent->path); if (l == -1) @@ -589,19 +710,29 @@ static void process_firmware_event(struct uevent *uevent) if(data_fd < 0) goto loading_close_out; +try_loading_again: fw_fd = open(file1, O_RDONLY); if(fw_fd < 0) { fw_fd = open(file2, O_RDONLY); if (fw_fd < 0) { + if (booting) { + /* If we're not fully booted, we may be missing + * filesystems needed for firmware, wait and retry. + */ + usleep(100000); + booting = is_booting(); + goto try_loading_again; + } + INFO("firmware: could not open '%s' %d\n", uevent->firmware, errno); write(loading_fd, "-1", 2); goto data_close_out; } } if(!load_firmware(fw_fd, loading_fd, data_fd)) - log_event_print("firmware copy success { '%s', '%s' }\n", root, uevent->firmware); + INFO("firmware: copy success { '%s', '%s' }\n", root, uevent->firmware); else - log_event_print("firmware copy failure { '%s', '%s' }\n", root, uevent->firmware); + INFO("firmware: copy failure { '%s', '%s' }\n", root, uevent->firmware); close(fw_fd); data_close_out: @@ -622,7 +753,6 @@ root_free_out: static void handle_firmware_event(struct uevent *uevent) { pid_t pid; - int status; int ret; if(strcmp(uevent->subsystem, "firmware")) @@ -636,10 +766,6 @@ static void handle_firmware_event(struct uevent *uevent) if (!pid) { process_firmware_event(uevent); exit(EXIT_SUCCESS); - } else { - do { - ret = waitpid(pid, &status, 0); - } while (ret == -1 && errno == EINTR); } } @@ -648,7 +774,7 @@ void handle_device_fd() { char msg[UEVENT_MSG_LEN+2]; int n; - while ((n = uevent_checked_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) { + while ((n = uevent_kernel_multicast_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) { if(n >= UEVENT_MSG_LEN) /* overflow -- discard */ continue; |