diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 18:29:04 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 18:29:04 -0800 |
commit | e54eebbf1a908d65ee8cf80bab62821c05666d70 (patch) | |
tree | 4b825dc642cb6eb9a060e54bf8d69288fbee4904 /init/devices.c | |
parent | a1e1c1b106423de09bc918502e7a51d4ffe5a4ae (diff) | |
download | core-e54eebbf1a908d65ee8cf80bab62821c05666d70.tar.gz core-e54eebbf1a908d65ee8cf80bab62821c05666d70.tar.bz2 core-e54eebbf1a908d65ee8cf80bab62821c05666d70.zip |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'init/devices.c')
-rw-r--r-- | init/devices.c | 626 |
1 files changed, 0 insertions, 626 deletions
diff --git a/init/devices.c b/init/devices.c deleted file mode 100644 index b1ef6ab2a..000000000 --- a/init/devices.c +++ /dev/null @@ -1,626 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/stat.h> -#include <sys/types.h> - -#include <fcntl.h> -#include <dirent.h> -#include <unistd.h> -#include <string.h> - -#include <sys/socket.h> -#include <sys/un.h> -#include <linux/netlink.h> -#include <private/android_filesystem_config.h> -#include <sys/time.h> -#include <asm/page.h> - -#include "init.h" -#include "devices.h" - -#define CMDLINE_PREFIX "/dev" -#define SYSFS_PREFIX "/sys" -#define FIRMWARE_DIR "/etc/firmware" -#define MAX_QEMU_PERM 6 - -struct uevent { - const char *action; - const char *path; - const char *subsystem; - const char *firmware; - int major; - int minor; -}; - -static int open_uevent_socket(void) -{ - struct sockaddr_nl addr; - int sz = 64*1024; // XXX larger? udev uses 16MB! - int s; - - memset(&addr, 0, sizeof(addr)); - addr.nl_family = AF_NETLINK; - addr.nl_pid = getpid(); - addr.nl_groups = 0xffffffff; - - s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); - if(s < 0) - return -1; - - setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)); - - if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - close(s); - return -1; - } - - return s; -} - -struct perms_ { - char *name; - mode_t perm; - unsigned int uid; - unsigned int gid; - unsigned short prefix; -}; -static struct perms_ devperms[] = { - { "/dev/null", 0666, AID_ROOT, AID_ROOT, 0 }, - { "/dev/zero", 0666, AID_ROOT, AID_ROOT, 0 }, - { "/dev/full", 0666, AID_ROOT, AID_ROOT, 0 }, - { "/dev/ptmx", 0666, AID_ROOT, AID_ROOT, 0 }, - { "/dev/tty", 0666, AID_ROOT, AID_ROOT, 0 }, - { "/dev/random", 0666, AID_ROOT, AID_ROOT, 0 }, - { "/dev/urandom", 0666, AID_ROOT, AID_ROOT, 0 }, - { "/dev/ashmem", 0666, AID_ROOT, AID_ROOT, 0 }, - { "/dev/binder", 0666, AID_ROOT, AID_ROOT, 0 }, - - /* logger should be world writable (for logging) but not readable */ - { "/dev/log/", 0662, AID_ROOT, AID_LOG, 1 }, - - /* these should not be world writable */ - { "/dev/android_adb", 0660, AID_ADB, AID_ADB, 0 }, - { "/dev/android_adb_enable", 0660, AID_ADB, AID_ADB, 0 }, - { "/dev/ttyMSM0", 0600, AID_BLUETOOTH, AID_BLUETOOTH, 0 }, - { "/dev/ttyHS0", 0600, AID_BLUETOOTH, AID_BLUETOOTH, 0 }, - { "/dev/uinput", 0600, AID_BLUETOOTH, AID_BLUETOOTH, 0 }, - { "/dev/alarm", 0664, AID_SYSTEM, AID_RADIO, 0 }, - { "/dev/tty0", 0660, AID_ROOT, AID_SYSTEM, 0 }, - { "/dev/graphics/", 0660, AID_ROOT, AID_GRAPHICS, 1 }, - { "/dev/hw3d", 0660, AID_SYSTEM, AID_GRAPHICS, 0 }, - { "/dev/input/", 0660, AID_ROOT, AID_INPUT, 1 }, - { "/dev/eac", 0660, AID_ROOT, AID_AUDIO, 0 }, - { "/dev/cam", 0660, AID_ROOT, AID_CAMERA, 0 }, - { "/dev/pmem", 0660, AID_SYSTEM, AID_GRAPHICS, 0 }, - { "/dev/pmem_gpu", 0660, AID_SYSTEM, AID_GRAPHICS, 1 }, - { "/dev/pmem_adsp", 0660, AID_SYSTEM, AID_AUDIO, 1 }, - { "/dev/pmem_camera", 0660, AID_SYSTEM, AID_CAMERA, 1 }, - { "/dev/oncrpc/", 0660, AID_ROOT, AID_SYSTEM, 1 }, - { "/dev/adsp/", 0660, AID_SYSTEM, AID_AUDIO, 1 }, - { "/dev/mt9t013", 0660, AID_SYSTEM, AID_SYSTEM, 0 }, - { "/dev/akm8976_daemon",0640, AID_COMPASS, AID_SYSTEM, 0 }, - { "/dev/akm8976_aot", 0640, AID_COMPASS, AID_SYSTEM, 0 }, - { "/dev/akm8976_pffd", 0640, AID_COMPASS, AID_SYSTEM, 0 }, - { "/dev/msm_pcm_out", 0660, AID_SYSTEM, AID_AUDIO, 1 }, - { "/dev/msm_pcm_in", 0660, AID_SYSTEM, AID_AUDIO, 1 }, - { "/dev/msm_pcm_ctl", 0660, AID_SYSTEM, AID_AUDIO, 1 }, - { "/dev/msm_snd", 0660, AID_SYSTEM, AID_AUDIO, 1 }, - { "/dev/msm_mp3", 0660, AID_SYSTEM, AID_AUDIO, 1 }, - { "/dev/msm_audpre", 0660, AID_SYSTEM, AID_AUDIO, 0 }, - { "/dev/htc-acoustic", 0660, AID_SYSTEM, AID_AUDIO, 0 }, - { "/dev/smd0", 0640, AID_RADIO, AID_RADIO, 0 }, - { "/dev/qmi", 0640, AID_RADIO, AID_RADIO, 0 }, - { "/dev/qmi0", 0640, AID_RADIO, AID_RADIO, 0 }, - { "/dev/qmi1", 0640, AID_RADIO, AID_RADIO, 0 }, - { "/dev/qmi2", 0640, AID_RADIO, AID_RADIO, 0 }, - { NULL, 0, 0, 0, 0 }, -}; - -/* devperms_partners list and perm_node are for hardware specific /dev entries */ -struct perm_node { - struct perms_ dp; - struct listnode plist; -}; -list_declare(devperms_partners); - -/* - * Permission override when in emulator mode, must be parsed before - * system properties is initalized. - */ -static int qemu_perm_count; -static struct perms_ qemu_perms[MAX_QEMU_PERM + 1]; - -int add_devperms_partners(const char *name, mode_t perm, unsigned int uid, - unsigned int gid, unsigned short prefix) { - int size; - struct perm_node *node = malloc(sizeof (struct perm_node)); - if (!node) - return -ENOMEM; - - size = strlen(name) + 1; - if ((node->dp.name = malloc(size)) == NULL) - return -ENOMEM; - - memcpy(node->dp.name, name, size); - node->dp.perm = perm; - node->dp.uid = uid; - node->dp.gid = gid; - node->dp.prefix = prefix; - - list_add_tail(&devperms_partners, &node->plist); - return 0; -} - -void qemu_init(void) { - qemu_perm_count = 0; - memset(&qemu_perms, 0, sizeof(qemu_perms)); -} - -static int qemu_perm(const char* name, mode_t perm, unsigned int uid, - unsigned int gid, unsigned short prefix) -{ - char *buf; - if (qemu_perm_count == MAX_QEMU_PERM) - return -ENOSPC; - - buf = malloc(strlen(name) + 1); - if (!buf) - return -errno; - - strlcpy(buf, name, strlen(name) + 1); - qemu_perms[qemu_perm_count].name = buf; - qemu_perms[qemu_perm_count].perm = perm; - qemu_perms[qemu_perm_count].uid = uid; - qemu_perms[qemu_perm_count].gid = gid; - qemu_perms[qemu_perm_count].prefix = prefix; - - qemu_perm_count++; - return 0; -} - -/* Permission overrides for emulator that are parsed from /proc/cmdline. */ -void qemu_cmdline(const char* name, const char *value) -{ - char *buf; - if (!strcmp(name, "android.ril")) { - /* cmd line params currently assume /dev/ prefix */ - if (asprintf(&buf, CMDLINE_PREFIX"/%s", value) == -1) { - return; - } - INFO("nani- buf:: %s\n", buf); - qemu_perm(buf, 0660, AID_RADIO, AID_ROOT, 0); - } -} - -static int get_device_perm_inner(struct perms_ *perms, const char *path, - unsigned *uid, unsigned *gid, mode_t *perm) -{ - int i; - for(i = 0; perms[i].name; i++) { - - if(perms[i].prefix) { - if(strncmp(path, perms[i].name, strlen(perms[i].name))) - continue; - } else { - if(strcmp(path, perms[i].name)) - continue; - } - *uid = perms[i].uid; - *gid = perms[i].gid; - *perm = perms[i].perm; - return 0; - } - return -1; -} - -/* First checks for emulator specific permissions specified in /proc/cmdline. */ -static mode_t get_device_perm(const char *path, unsigned *uid, unsigned *gid) -{ - mode_t perm; - - if (get_device_perm_inner(qemu_perms, path, uid, gid, &perm) == 0) { - return perm; - } else if (get_device_perm_inner(devperms, path, uid, gid, &perm) == 0) { - return perm; - } else { - struct listnode *node; - struct perm_node *perm_node; - struct perms_ *dp; - - /* Check partners list. */ - list_for_each(node, &devperms_partners) { - perm_node = node_to_item(node, struct perm_node, plist); - dp = &perm_node->dp; - - if (dp->prefix) { - if (strncmp(path, dp->name, strlen(dp->name))) - continue; - } else { - if (strcmp(path, dp->name)) - continue; - } - /* Found perm in partner list. */ - *uid = dp->uid; - *gid = dp->gid; - return dp->perm; - } - /* Default if nothing found. */ - *uid = 0; - *gid = 0; - return 0600; - } -} - -static void make_device(const char *path, int block, int major, int minor) -{ - unsigned uid; - unsigned gid; - mode_t mode; - dev_t dev; - - if(major > 255 || minor > 255) - return; - - mode = get_device_perm(path, &uid, &gid) | (block ? S_IFBLK : S_IFCHR); - dev = (major << 8) | minor; - mknod(path, mode, dev); - chown(path, uid, gid); -} - -#ifdef LOG_UEVENTS - -static inline suseconds_t get_usecs(void) -{ - struct timeval tv; - gettimeofday(&tv, 0); - return tv.tv_sec * (suseconds_t) 1000000 + tv.tv_usec; -} - -#define log_event_print(x...) INFO(x) - -#else - -#define log_event_print(fmt, args...) do { } while (0) -#define get_usecs() 0 - -#endif - -static void parse_event(const char *msg, struct uevent *uevent) -{ - uevent->action = ""; - uevent->path = ""; - uevent->subsystem = ""; - uevent->firmware = ""; - uevent->major = -1; - uevent->minor = -1; - - /* currently ignoring SEQNUM */ - while(*msg) { - if(!strncmp(msg, "ACTION=", 7)) { - msg += 7; - uevent->action = msg; - } else if(!strncmp(msg, "DEVPATH=", 8)) { - msg += 8; - uevent->path = msg; - } else if(!strncmp(msg, "SUBSYSTEM=", 10)) { - msg += 10; - uevent->subsystem = msg; - } else if(!strncmp(msg, "FIRMWARE=", 9)) { - msg += 9; - uevent->firmware = msg; - } else if(!strncmp(msg, "MAJOR=", 6)) { - msg += 6; - uevent->major = atoi(msg); - } else if(!strncmp(msg, "MINOR=", 6)) { - msg += 6; - uevent->minor = atoi(msg); - } - - /* advance to after the next \0 */ - while(*msg++) - ; - } - - log_event_print("event { '%s', '%s', '%s', '%s', %d, %d }\n", - uevent->action, uevent->path, uevent->subsystem, - uevent->firmware, uevent->major, uevent->minor); -} - -static void handle_device_event(struct uevent *uevent) -{ - char devpath[96]; - char *base, *name; - int block; - - /* if it's not a /dev device, nothing 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); - } else { - block = 0; - /* this should probably be configurable somehow */ - 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, "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, "misc", 4) && - !strncmp(name, "log_", 4)) { - base = "/dev/log/"; - mkdir(base, 0755); - name += 4; - } else - base = "/dev/"; - } - - snprintf(devpath, sizeof(devpath), "%s%s", base, name); - - if(!strcmp(uevent->action, "add")) { - make_device(devpath, block, uevent->major, uevent->minor); - return; - } - - if(!strcmp(uevent->action, "remove")) { - unlink(devpath); - return; - } -} - -static int load_firmware(int fw_fd, int loading_fd, int data_fd) -{ - struct stat st; - long len_to_copy; - int ret = 0; - - if(fstat(fw_fd, &st) < 0) - return -1; - len_to_copy = st.st_size; - - write(loading_fd, "1", 1); /* start transfer */ - - while (len_to_copy > 0) { - char buf[PAGE_SIZE]; - ssize_t nr; - - nr = read(fw_fd, buf, sizeof(buf)); - if(!nr) - break; - if(nr < 0) { - ret = -1; - break; - } - - len_to_copy -= nr; - while (nr > 0) { - ssize_t nw = 0; - - nw = write(data_fd, buf + nw, nr); - if(nw <= 0) { - ret = -1; - goto out; - } - nr -= nw; - } - } - -out: - if(!ret) - write(loading_fd, "0", 1); /* successful end of transfer */ - else - write(loading_fd, "-1", 2); /* abort transfer */ - - return ret; -} - -static void process_firmware_event(struct uevent *uevent) -{ - char *root, *loading, *data, *file; - int l, loading_fd, data_fd, fw_fd; - - log_event_print("firmware event { '%s', '%s' }\n", - uevent->path, uevent->firmware); - - l = asprintf(&root, SYSFS_PREFIX"%s/", uevent->path); - if (l == -1) - return; - - l = asprintf(&loading, "%sloading", root); - if (l == -1) - goto root_free_out; - - l = asprintf(&data, "%sdata", root); - if (l == -1) - goto loading_free_out; - - l = asprintf(&file, FIRMWARE_DIR"/%s", uevent->firmware); - if (l == -1) - goto data_free_out; - - loading_fd = open(loading, O_WRONLY); - if(loading_fd < 0) - goto file_free_out; - - data_fd = open(data, O_WRONLY); - if(data_fd < 0) - goto loading_close_out; - - fw_fd = open(file, O_RDONLY); - if(fw_fd < 0) - goto data_close_out; - - if(!load_firmware(fw_fd, loading_fd, data_fd)) - log_event_print("firmware copy success { '%s', '%s' }\n", root, file); - else - log_event_print("firmware copy failure { '%s', '%s' }\n", root, file); - - close(fw_fd); -data_close_out: - close(data_fd); -loading_close_out: - close(loading_fd); -file_free_out: - free(file); -data_free_out: - free(data); -loading_free_out: - free(loading); -root_free_out: - free(root); -} - -static void handle_firmware_event(struct uevent *uevent) -{ - pid_t pid; - - if(strcmp(uevent->subsystem, "firmware")) - return; - - if(strcmp(uevent->action, "add")) - return; - - /* we fork, to avoid making large memory allocations in init proper */ - pid = fork(); - if (!pid) { - process_firmware_event(uevent); - exit(EXIT_SUCCESS); - } -} - -#define UEVENT_MSG_LEN 1024 -void handle_device_fd(int fd) -{ - char msg[UEVENT_MSG_LEN+2]; - int n; - - while((n = recv(fd, msg, UEVENT_MSG_LEN, 0)) > 0) { - struct uevent uevent; - - if(n == UEVENT_MSG_LEN) /* overflow -- discard */ - continue; - - msg[n] = '\0'; - msg[n+1] = '\0'; - - parse_event(msg, &uevent); - - handle_device_event(&uevent); - handle_firmware_event(&uevent); - } -} - -/* Coldboot walks parts of the /sys tree and pokes the uevent files -** to cause the kernel to regenerate device add events that happened -** before init's device manager was started -** -** We drain any pending events from the netlink socket every time -** we poke another uevent file to make sure we don't overrun the -** socket's buffer. -*/ - -static void do_coldboot(int event_fd, DIR *d) -{ - struct dirent *de; - int dfd, fd; - - dfd = dirfd(d); - - fd = openat(dfd, "uevent", O_WRONLY); - if(fd >= 0) { - write(fd, "add\n", 4); - close(fd); - handle_device_fd(event_fd); - } - - while((de = readdir(d))) { - DIR *d2; - - if(de->d_type != DT_DIR || de->d_name[0] == '.') - continue; - - fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY); - if(fd < 0) - continue; - - d2 = fdopendir(fd); - if(d2 == 0) - close(fd); - else { - do_coldboot(event_fd, d2); - closedir(d2); - } - } -} - -static void coldboot(int event_fd, const char *path) -{ - DIR *d = opendir(path); - if(d) { - do_coldboot(event_fd, d); - closedir(d); - } -} - -int device_init(void) -{ - suseconds_t t0, t1; - int fd; - - fd = open_uevent_socket(); - if(fd < 0) - return -1; - - fcntl(fd, F_SETFD, FD_CLOEXEC); - fcntl(fd, F_SETFL, O_NONBLOCK); - - t0 = get_usecs(); - coldboot(fd, "/sys/class"); - coldboot(fd, "/sys/block"); - coldboot(fd, "/sys/devices"); - t1 = get_usecs(); - - log_event_print("coldboot %ld uS\n", ((long) (t1 - t0))); - - return fd; -} |