ADROID 2.1 架构解析 9 SD/USBk

9 SD/USB

9.1 主流程

文件:system/core/vold/Vold.c
int main(int argc, char **argv)
{
       ...
mkdir("/dev/block/vold", 0755);
       ...
        /*
     * Bootstrap
     */
 
    bootstrap = 1;
    // Volume Manager
    volmgr_bootstrap();
 
    // SD Card system
    mmc_bootstrap();
 
       ...
    // Switch
    switch_bootstrap();
 
    bootstrap = 0;
       ...
}
volmgr_bootstrap : 加载配置文件
mmc_bootstrap    : 挂载mmc/sdcard
switch_bootstrap   : 连接usb
9.2 加载配置文件

文件:system/core/vold/Volmgr.c
int volmgr_bootstrap(void)
{
    int rc;
 
    if ((rc = volmgr_readconfig("/system/etc/vold.conf")) < 0) {
        LOGE("Unable to process config");
        return rc;
    }
 
    /*
     * Check to see if any of our volumes is mounted
     */
    volume_t *v = vol_root;
    while (v) {
        if (_mountpoint_mounted(v->mount_point)) {
            LOGW("Volume '%s' already mounted at startup", v->mount_point);
            v->state = volstate_mounted;
        }
        v = v->next;
    }
 
    return 0;
}
 
两部分功能:
1 读取配置文件:/system/etc/vold.conf
2 用_mountpoint_mounted检查设备是否挂载,若挂载则状态改为volstate_mounted
 
static int volmgr_readconfig(char *cfg_path)
{
    cnode *root = config_node("", "");
    cnode *node;
 
    config_load_file(root, cfg_path);
    node = root->first_child;
 
    while (node) {
        if (!strncmp(node->name, "volume_", 7))
            volmgr_config_volume(node);
        else
            LOGE("Skipping unknown configuration node '%s'", node->name);
        node = node->next;
    }
    return 0;
}
9.2.1 读取配置文件

config_load_file的功能是将配置文件的信息读出来,然后以cnode链表结构的方式保存。
cnode 结构如下:
struct cnode
{
    cnode *next;
    cnode *first_child;
    cnode *last_child;
    const char *name;
    const char *value;
};
如配置文件:/system/etc/vold.conf
volume_sdcard {
    ## This is the direct uevent device path to the SD slot on the device
    media_path     /devices/platform/msm_sdcc.2/mmc_host/mmc1
    emu_media_path /devices/platform/goldfish_mmc.0/mmc_host/mmc0
 
    media_type     mmc
    mount_point    /sdcard
    ums_path       /devices/platform/usb_mass_storage/lun0
}
读到链表后的形式是:
Root ---- first_child ---- name = volume_sdcard
                |--- next ---- name = media_path
                                      |--- value = /devices/platform/msm_sdcc.2/mmc_host/mmc1
                                      |--- next ---- name = emu_media_path
                                                |--- value = /devices/platform/goldfish_mmc.0 ...
                                                   |--- next ---- ...
按照这样的格式保存配置文件的信息。
9.2.2 分析配置文件

volmgr_config_volume 的功能是将root链表结构的信息存储到vol_root链表结构对应的项里。
vol_root结构如下:
typedef struct volume {
    char            *media_paths[VOLMGR_MAX_MEDIAPATHS_PER_VOLUME];
 
    media_type_t      media_type;
    char              *mount_point;
    char              *ums_path;
    struct devmapping *dm;
 
    pthread_mutex_t          lock;
    volume_state_t           state;
    blkdev_t                 *dev;
    pid_t                    worker_pid;
    pthread_t                worker_thread;
    union {
        struct volmgr_start_args  start_args;
        struct volmgr_reaper_args reaper_args;
    } worker_args;
    boolean                  worker_running;
    pthread_mutex_t          worker_sem;
 
    struct volmgr_fstable_entry *fs;
 
    struct volume            *next;
} volume_t;
也就是说用media_paths、media_type、media_type、mount_point、ums_path等来存储配置文件里对应的值。
9.3挂载mmc/sdcard

文件:system/core/vold/Mmc.c
#define SYSFS_CLASS_MMC_PATH "/sys/class/mmc_host"
int mmc_bootstrap()
{
    DIR *d;
    struct dirent *de;
 
    if (!(d = opendir(SYSFS_CLASS_MMC_PATH))) {
        LOG_ERROR("Unable to open '%s' (%s)", SYSFS_CLASS_MMC_PATH,
                  strerror(errno));
        return -errno;
    }
 
    while ((de = readdir(d))) {
        char tmp[255];
 
        if (de->d_name[0] == '.')
            continue;
 
        sprintf(tmp, "%s/%s", SYSFS_CLASS_MMC_PATH, de->d_name);
        if (mmc_bootstrap_controller(tmp)) {
            LOG_ERROR("Error bootstrapping controller '%s' (%s)", tmp,
                      strerror(errno));
        }
    }
 
    closedir(d);
 
    return 0;
}
 
以下面/sys/class/mmc_host目录为例加以解说。
例子:
/sys/class/mmc_host/: mmc0 mmc1
/sys/class/mmc_host/mmc0/: uevent subsystem device power mmc0:e624
Mmc0:e624是一个链接目录,其真实路径是:
/sys/devices/platform/pxa2xx-mci.0/mmc_host/mmc0/mmc0:e624
/sys/devices/platform/pxa2xx-mci.0/mmc_host/mmc0/mmc0:e624/name: SR016
/sys/devices/platform/pxa2xx-mci.0/mmc_host/mmc0/mmc0:e624/type:SD
/sys/devices/platform/pxa2xx-mci.0/mmc_host/mmc0/mmc0:e624/block/:mmcblk0
/sys/devices/platform/pxa2xx-mci.0/mmc_host/mmc0/mmc0:e624/block/mmcblk0/:
uevent  dev  subsystem  device  range  ext_range  removable  ro  size  capability
stat  power  holders  slaves  mmcblk0p1  queue  bdi
 
上面代码实现的功能是:扫描/sys/class/mmc_hos目录下的所有文件和文件夹,然后一个一个的将它的路径传入mmc_bootstrap_controller, 如下,以例子路径来说,则会先把/sys/class/mmc_host/mmc0传给下面函数:
static int mmc_bootstrap_controller(char *sysfs_path)
{
    DIR *d;
    struct dirent *de;
 
#if DEBUG_BOOTSTRAP
    LOG_VOL("bootstrap_controller(%s):", sysfs_path);
#endif
    if (!(d = opendir(sysfs_path))) {
        LOG_ERROR("Unable to open '%s' (%s)", sysfs_path, strerror(errno));
        return -errno;
    }
 
    while ((de = readdir(d))) {
        char tmp[255];
 
        if (de->d_name[0] == '.')
            continue;
 
        if ((!strcmp(de->d_name, "uevent")) ||
            (!strcmp(de->d_name, "subsystem")) ||
            (!strcmp(de->d_name, "device")) ||
            (!strcmp(de->d_name, "power"))) {
            continue;
        }
 
        sprintf(tmp, "%s/%s", sysfs_path, de->d_name);
 
        if (mmc_bootstrap_card(tmp) < 0)
            LOG_ERROR("Error bootstrapping card '%s' (%s)", tmp, strerror(errno));
    } // while
 
    closedir(d);
return 0; 
}
继续扫描传进来的路径,将文件名不在{uevent,subsystem,device,power}内的文件或文件夹传给mmc_bootstrap_card,如下,以例子路径来说,则会先把/sys/class/mmc_host/mmc0/ mmc0:e624传给下面函数
static int mmc_bootstrap_card(char *sysfs_path)
{
    char saved_cwd[255];
    char new_cwd[255];
    char *devpath;
    char *uevent_params[4];
    char *p;
    char filename[255];
    char tmp[255];
    ssize_t sz;
 
#if DEBUG_BOOTSTRAP
    LOG_VOL("bootstrap_card(%s):", sysfs_path);
#endif
 
    /*
     * sysfs_path is based on /sys/class, but we want the actual device class
     */
    if (!getcwd(saved_cwd, sizeof(saved_cwd))) {
        LOGE("Error getting working dir path");
        return -errno;
    }
  
    if (chdir(sysfs_path) < 0) {
        LOGE("Unable to chdir to %s (%s)", sysfs_path, strerror(errno));
        return -errno;
    }
 
    if (!getcwd(new_cwd, sizeof(new_cwd))) {
        LOGE("Buffer too small for device path");
        return -errno;
    }
 
    if (chdir(saved_cwd) < 0) {
        LOGE("Unable to restore working dir");
        return -errno;
    }
 
    devpath = &new_cwd[4]; // Skip over '/sys'
 
    /*
     * Collect parameters so we can simulate a UEVENT
     */
    sprintf(tmp, "DEVPATH=%s", devpath);
    uevent_params[0] = (char *) strdup(tmp);
 
    sprintf(filename, "/sys%s/type", devpath);
    p = read_file(filename, &sz);
    p[strlen(p) - 1] = '/0';
    sprintf(tmp, "MMC_TYPE=%s", p);
    free(p);
    uevent_params[1] = (char *) strdup(tmp);
 
    sprintf(filename, "/sys%s/name", devpath);
    p = read_file(filename, &sz);
    p[strlen(p) - 1] = '/0';
    sprintf(tmp, "MMC_NAME=%s", p);
    free(p);
    uevent_params[2] = (char *) strdup(tmp);
 
    uevent_params[3] = (char *) NULL;
 
    if (simulate_uevent("mmc", devpath, "add", uevent_params) < 0) {
        LOGE("Error simulating uevent (%s)", strerror(errno));
        return -errno;
    }
 
    /*
     *  Check for block drivers
     */
    char block_devpath[255];
    sprintf(tmp, "%s/block", devpath);
    sprintf(filename, "/sys%s/block", devpath);
    if (!access(filename, F_OK)) {
        if (mmc_bootstrap_block(tmp)) {
            LOGE("Error bootstrapping block @ %s", tmp);
        }
    }
 
    return 0;
}
Getcwd 获取当前路径
Chdir 更改路径
由于sysfs_path是一个链接路径,所以需要用Chdir和Getcwd来获取它的真实路径。以例子路径来说,获得的真实路径是:
/sys/devices/platform/pxa2xx-mci.0/mmc_host/mmc0/mmc0:e624
所以DEVPATH = /devices/platform/pxa2xx-mci.0/mmc_host/mmc0/mmc0:e624,而这个也就是在配置文件vold.conf里media_path的路径,media_path的路径要与DEVPATH相同,否则不会加载SD卡,这个<<9.5.1.3处理uevent 事件>>说明。
DEVPATH = /devices/platform/pxa2xx-mci.0/mmc_host/mmc0/mmc0:e624
MMC_TYPE=SD
MMC_NAME= SR016
以上三个参数作uevent的参数,虚拟产生一个add事件:
simulate_uevent("mmc", devpath, "add", uevent_params);
然后将路径 /devices/platform/pxa2xx-mci.0/mmc_host/mmc0/mmc0:e624/block
传给mmc_bootstrap_block,如下
static int mmc_bootstrap_block(char *devpath)
{
    char blockdir_path[255];
    DIR *d;
    struct dirent *de;
 
#if DEBUG_BOOTSTRAP
    LOG_VOL("mmc_bootstrap_block(%s):", devpath);
#endif
 
    sprintf(blockdir_path, "/sys%s", devpath);
 
    if (!(d = opendir(blockdir_path))) {
        LOGE("Failed to opendir %s", devpath);
        return -errno;
    }
 
    while ((de = readdir(d))) {
        char tmp[255];
 
        if (de->d_name[0] == '.')
            continue;
        sprintf(tmp, "%s/%s", devpath, de->d_name);
        if (mmc_bootstrap_mmcblk(tmp))
            LOGE("Error bootstraping mmcblk @ %s", tmp);
    }
    closedir(d);
    return 0;
}
会扫描所有文件或目录,将路径传给mmc_bootstrap_mmcblk,以例子路径来说,则会把/sys/devices/platform/pxa2xx-mci.0/mmc_host/mmc0/mmc0:e624/block/mmcblk0/传给下面函数
static int mmc_bootstrap_mmcblk(char *devpath)
{
    char *mmcblk_devname;
    int part_no;
    int rc;
 
#if DEBUG_BOOTSTRAP
    LOG_VOL("mmc_bootstrap_mmcblk(%s):", devpath);
#endif
 
    if ((rc = mmc_bootstrap_mmcblk_partition(devpath))) {
        LOGE("Error bootstrapping mmcblk partition '%s'", devpath);
        return rc;
    }
 
    for (mmcblk_devname = &devpath[strlen(devpath)];
         *mmcblk_devname != '/'; mmcblk_devname--);
    mmcblk_devname++;
 
    for (part_no = 0; part_no < 4; part_no++) {
        char part_file[255];
        sprintf(part_file, "/sys%s/%sp%d", devpath, mmcblk_devname, part_no);
        if (!access(part_file, F_OK)) {
            char part_devpath[255];
 
            sprintf(part_devpath, "%s/%sp%d", devpath, mmcblk_devname, part_no);
            if (mmc_bootstrap_mmcblk_partition(part_devpath))
                LOGE("Error bootstrapping mmcblk partition '%s'", part_devpath);
        }
    }
 
    return 0;
}
mmc_bootstrap_mmcblk_partition的功能是读取当前路径下的uevent文件里的四个参数:
DEVPATH,DEVTYPE、MAJOR、MINOR,然后将这四个参数作为uevent参数,产生一个uevent事件:
simulate_uevent("block", devpath, "add", uevent_params);
所以上面代码的功能是:
1 用/sys/devices/platform/pxa2xx-mci.0/mmc_host/mmc0/mmc0:e624/block/mmcblk0/uevent里的参数产生一个uevent事件
2 /sys/devices/platform/pxa2xx-mci.0/mmc_host/mmc0/mmc0:e624/block/mmcblk0/mmcblk0p0
 /sys/devices/platform/pxa2xx-mci.0/mmc_host/mmc0/mmc0:e624/block/mmcblk0/mmcblk0p1
 /sys/devices/platform/pxa2xx-mci.0/mmc_host/mmc0/mmc0:e624/block/mmcblk0/mmcblk0p2
 /sys/devices/platform/pxa2xx-mci.0/mmc_host/mmc0/mmc0:e624/block/mmcblk0/mmcblk0p3
以上路径如存在,则读取目录下的uevent文件,产生一个uevent事件.
 
所以,加载这部分的功能就是产生三个事件:
devpath=/devices/platform/pxa2xx-mci.0/mmc_host/mmc0/mmc0:e624
simulate_uevent("mmc", devpath, "add", uevent_params);
 
devpath=/devices/platform/pxa2xx-mci.0/mmc_host/mmc0/mmc0:e624/block/mmcblk0/
simulate_uevent("block", devpath, "add", uevent_params);
 
devpath=/devices/platform/pxa2xx-mci.0/mmc_host/mmc0/mmc0:e624/block/mmcblk0/mmcblk0p1
simulate_uevent("block", devpath, "add", uevent_params);
9.4连接usb

文件:system/core/vold/Switch.c
#define SYSFS_CLASS_SWITCH_PATH "/sys/class/switch"
int switch_bootstrap()
{
    DIR *d;
    struct dirent *de;
 
    if (!(d = opendir(SYSFS_CLASS_SWITCH_PATH))) {
        LOG_ERROR("Unable to open '%s' (%s)", SYSFS_CLASS_SWITCH_PATH,
                   strerror(errno));
        return -errno;
    }
 
    while ((de = readdir(d))) {
        char tmp[255];
 
        if (de->d_name[0] == '.')
            continue;
 
        sprintf(tmp, "%s/%s", SYSFS_CLASS_SWITCH_PATH, de->d_name);
        if (mmc_bootstrap_switch(tmp)) {
            LOG_ERROR("Error bootstrapping switch '%s' (%s)", tmp,
                      strerror(errno));
        }
    }
 
    closedir(d);
 
    return 0;
}
扫描路径/sys/class/switch下的文件和目录,并将路径传给函数mmc_bootstrap_switch.
以下/sys/class/switch目录为例,来说明这个过程.
 
例子:
/sys/class/switch/
micco_hsdetect                                                                
usb_mass_storage
 
/sys/class/switch/ usb_mass_storage/
uevent                                                                        
subsystem                                                                      
power                                                                         
state                                                                         
name
/sys/class/switch/ usb_mass_storage/name:usb_mass_storage
 
/sys/devices/virtual/switch/
micco_hsdetect                                                                
usb_mass_storage
 
/sys/devices/virtual/switch/usb_mass_storage
uevent                                                                         
subsystem                                                                     
power                                                                         
state                                                                          
name
/sys/devices/virtual/switch/usb_mass_storage/state:online
 
以上代码实现的功能是分别将
/sys/class/switch/micco_hsdetect
/sys/class/switch/usb_mass_storage
两个路径传给函数mmc_bootstrap_switch
static int mmc_bootstrap_switch(char *sysfs_path)
{
#if DEBUG_BOOTSTRAP
    LOG_VOL("bootstrap_switch(%s):", sysfs_path);
#endif
 
    char filename[255];
    char name[255];
    char state[255];
    char tmp[255];
    char *uevent_params[3];
    char devpath[255];
    FILE *fp;
 
    /*
     * Read switch name
     */
    sprintf(filename, "%s/name", sysfs_path);
    if (!(fp = fopen(filename, "r"))) {
        LOGE("Error opening switch name path '%s' (%s)",
             sysfs_path, strerror(errno));
       return -errno;
    }
    if (!fgets(name, sizeof(name), fp)) {
        LOGE("Unable to read switch name");
        fclose(fp);
        return -EIO;
    }
    fclose(fp);
 
    name[strlen(name) -1] = '/0';
    sprintf(devpath, "/devices/virtual/switch/%s", name);
    sprintf(tmp, "SWITCH_NAME=%s", name);
    uevent_params[0] = (char *) strdup(tmp);
 
    /*
     * Read switch state
     */
    sprintf(filename, "%s/state", sysfs_path);
    if (!(fp = fopen(filename, "r"))) {
        LOGE("Error opening switch state path '%s' (%s)",
             sysfs_path, strerror(errno));
       return -errno;
    }
    if (!fgets(state, sizeof(state), fp)) {
        LOGE("Unable to read switch state");
        fclose(fp);
        return -EIO;
    }
    fclose(fp);
 
    state[strlen(state) -1] = '/0';
    sprintf(tmp, "SWITCH_STATE=%s", state);
    uevent_params[1] = (char *) strdup(tmp);
 
    uevent_params[2] = (char *) NULL;
 
    if (simulate_uevent("switch", devpath, "add", uevent_params) < 0) {
        LOGE("Error simulating uevent (%s)", strerror(errno));
        return -errno;
    }
 
    return 0; 
}
 
转到/sys/class/switch/usb_mass_storage下,获取state的值,再作为uevent的参数,产生switch 的uevent事件。
SWITCH_NAME=usb_mass_storage
SWITCH_STATE=online
devpath=/devices/virtual/switch/usb_mass_storage
9.5 通信机制

9.5.1 uevent

文件:system/core/vold/Uevent.c
9.5.1.1 产生uevent事件

int simulate_uevent(char *subsys, char *path, char *action, char **params)
{
    struct uevent *event;
    char tmp[255];
    int i, rc;
 
    if (!(event = malloc(sizeof(struct uevent)))) {
        LOGE("Error allocating memory (%s)", strerror(errno));
        return -errno;
    }
 
    memset(event, 0, sizeof(struct uevent));
 
    event->subsystem = strdup(subsys);
 
    if (!strcmp(action, "add"))
        event->action = action_add;
    else if (!strcmp(action, "change"))
        event->action = action_change;
    else if (!strcmp(action, "remove"))
        event->action = action_remove;
    else {
        LOGE("Invalid action '%s'", action);
        return -1;
    }
 
    event->path = strdup(path);
 
    for (i = 0; i < UEVENT_PARAMS_MAX; i++) {
        if (!params[i])
            break;
        event->param[i] = strdup(params[i]);
    }
 
    rc = dispatch_uevent(event);
    free_uevent(event);
    return rc;
}
如:
devpath = “/devices/platform/pxa2xx-mci.0/mmc_host/mmc0/mmc0:e624”
uevent_params [0] = “MMC_TYPE=SD”
uevent_params [1] = “MMC_NAME= SR016”
simulate_uevent("mmc", devpath, "add", uevent_params);
则经过simulate_uevent后,打包成:
event->subsystem        =  “mmc”
event->action              =  action_add;
event->path                  =  “/devices/platform/pxa2xx-mci.0/mmc_host/mmc0/mmc0:e624”
event->param[0]           =  “MMC_TYPE=SD”
event->param[1]           =  “MMC_NAME=SR016”
然后发给dispatch_uevent。
9.5.1.2 分配uevent事件

static struct uevent_dispatch dispatch_table[] = {
    { "switch", handle_switch_event },
    { "battery", handle_battery_event },
    { "mmc", handle_mmc_event },
    { "block", handle_block_event },
    { "bdi", handle_bdi_event },
    { "power_supply", handle_powersupply_event },
    { NULL, NULL }
};
static int dispatch_uevent(struct uevent *event)
{
    int i;
 
#if DEBUG_UEVENT
    dump_uevent(event);
#endif
    for (i = 0; dispatch_table[i].subsystem != NULL; i++) {
        if (!strcmp(dispatch_table[i].subsystem, event->subsystem))
            return dispatch_table[i].dispatch(event);
    }
 
#if DEBUG_UEVENT
    LOG_VOL("No uevent handlers registered for '%s' subsystem", event->subsystem);
#endif
    return 0;
}
根据subsystem的类型来分配,event->subsystem         =  “mmc”,则分配到handle_mmc_event进行处理
9.5.1.3处理uevent 事件

以mmc加载事件为例:
处理  << 9.3挂载mmc/sdcard>>的    simulate_uevent("mmc", devpath, "add", uevent_params);
文件:system/core/vold/uevent.c
static int handle_mmc_event(struct uevent *event)
{
    if (event->action == action_add) {
        media_t *media;
        char serial[80];
        char *type;
 
        /*
         * Pull card information from sysfs
         */
        type = get_uevent_param(event, "MMC_TYPE");
        if (strcmp(type, "SD") && strcmp(type, "MMC"))
            return 0;
      
        read_sysfs_var(serial, sizeof(serial), event->path, "serial");
        if (!(media = media_create(event->path,
                                   get_uevent_param(event, "MMC_NAME"),
                                   serial,
                                   media_mmc))) {
            LOGE("Unable to allocate new media (%s)", strerror(errno));
            return -1;
        }
        LOGI("New MMC card '%s' (serial %u) added @ %s", media->name,
                  media->serial, media->devpath);
}
...
    }
 
    return 0;
}
文件:system/core/vold/Media.c
media_t *media_create(char *devpath, char *name, char *serial, media_type_t type)
{
    media_list_t *list_entry;
    media_t *new;
 
    if (!(new = malloc(sizeof(media_t))))
        return NULL;
 
    memset(new, 0, sizeof(media_t));
 
    if (!(list_entry = malloc(sizeof(media_list_t)))) {
        free(new);
        return NULL;
    }
    list_entry->media = new;
    list_entry->next = NULL;
 
    if (!list_root)
        list_root = list_entry;
    else {
        media_list_t *list_scan = list_root;
        while(list_scan->next)
            list_scan = list_scan->next;
        list_scan->next = list_entry;
    }
   
    new->devpath = strdup(devpath);
    new->name = strdup(name);
    if (!serial)
        new->serial = 0;
    else
        new->serial = strtoul(serial, NULL, 0);
 
    new->media_type = type;
 
    return new;
}
将devpath, name, serial, media_type等信息以media结构打包放到list_root链表里去。
处理<< 9.3挂载mmc/sdcard>>的simulate_uevent("block", devpath, "add", uevent_params);其中有两个block的事件,对应两个目录,所以有 disk,partition两个参数,处理如下:
文件:system/core/vold/uevent.c
static int handle_block_event(struct uevent *event)
{
    char mediapath[255];
    media_t *media;
    int n;
    int maj, min;
    blkdev_t *blkdev;
    char *mmcblk_devname;
    /*
     * Look for backing media for this block device
     */
    if (!strncmp(get_uevent_param(event, "DEVPATH"),
                 "/devices/virtual/",
                 strlen("/devices/virtual/"))) {
        n = 0;
    } else if (!strcmp(get_uevent_param(event, "DEVTYPE"), "disk"))
        n = 2;
    else if (!strcmp(get_uevent_param(event, "DEVTYPE"), "partition"))
        n = 3;
    else {
        LOGE("Bad blockdev type '%s'", get_uevent_param(event, "DEVTYPE"));
        return -EINVAL;
    }
 
    truncate_sysfs_path(event->path, n, mediapath, sizeof(mediapath));
 
    LOGE("PATH=  '%s' ,n=%d,mediapth = %s",event->path, n, mediapath);
 
    if (!(media = media_lookup_by_path(mediapath, false))) {
#if DEBUG_UEVENT
        LOG_VOL("No backend media found @ device path '%s'", mediapath);
#endif
        return 0;
    }
 
    maj = atoi(get_uevent_param(event, "MAJOR"));
    min = atoi(get_uevent_param(event, "MINOR"));
 
    if (event->action == action_add) {
        blkdev_t *disk;
 
        /*
         * If there isn't a disk already its because *we*
         * are the disk
         */
        if (media->media_type == media_mmc)
            disk = blkdev_lookup_by_devno(maj, ALIGN_MMC_MINOR(min));
        else
            disk = blkdev_lookup_by_devno(maj, 0);
 
        if (!(blkdev = blkdev_create(disk,
                                     event->path,
                                     maj,
                                     min,
                                     media,
                                     get_uevent_param(event, "DEVTYPE")))) {
            LOGE("Unable to allocate new blkdev (%s)", strerror(errno));
            return -1;
        }
 
        blkdev_refresh(blkdev);
 
        /*
         * Add the blkdev to media
         */
        int rc;
        if ((rc = media_add_blkdev(media, blkdev)) < 0) {
            LOGE("Unable to add blkdev to card (%d)", rc);
            return rc;
        }
 
        LOGI("New blkdev %d.%d on media %s, media path %s, Dpp %d",
                blkdev->major, blkdev->minor, media->name, mediapath,
                blkdev_get_num_pending_partitions(blkdev->disk));
 
        if (blkdev_get_num_pending_partitions(blkdev->disk) == 0) {
            if ((rc = volmgr_consider_disk(blkdev->disk)) < 0) {
                if (rc == -EBUSY) {
                    LOGI("Volmgr not ready to handle device");
                } else {
                    LOGE("Volmgr failed to handle device (%d)", rc);
                    return rc;
                }
            }
        }
}
...
    return 0;
}
truncate_sysfs_path 的功能是后通n个目录,为了最终获得路径:
/devices/platform/pxa2xx-mci.0/mmc_host/mmc0/mmc0:e624         
blkdev_create 的功能是将major,minor,media等参数组成一个blkdev然后返回blkdev。
media_add_blkdev 的功能是将blkdev添加到list_root列表去
media_lookup_by_path的功能是与media_create创建后的media_path行比较,确定是否一致。
volmgr_consider_disk的功能是list_root的media_path与vol_root的media_path行比较,确定是否一致。
 
文件:system/core/vold/volmgr.c
int volmgr_consider_disk(blkdev_t *dev)
{
    volume_t *vol;
 
    if (!(vol = volmgr_lookup_volume_by_mediapath(dev->media->devpath, true)))
{
LOGE("volmgr_consider_disk:LOOKUP FAILED");
        return 0;
    }
   ...
}
static volume_t *volmgr_lookup_volume_by_mediapath(char *media_path, boolean fuzzy)
{
    volume_t *scan = vol_root;
    int i;
 
    while (scan) {
 
        for (i = 0; i < VOLMGR_MAX_MEDIAPATHS_PER_VOLUME; i++) {
            if (!scan->media_paths[i])
                continue;
 
            if (fuzzy && !strncmp(media_path, scan->media_paths[i], strlen(scan->media_paths[i])))
                return scan;
            else if (!fuzzy && !strcmp(media_path, scan->media_paths[i]))
                return scan;
        }
 
        scan = scan->next;
    }
    return NULL;
}
如上代码,会比较strncmp(media_path, scan->media_paths[i], strlen(scan->media_paths[i]),会以vol_root的media_path的长度来比较media_path的路径是否与devpath一致。
比如,media_path = /devices/platform/pxa2xx-mci.0/mmc_host/mmc0/mmc0:e624
则,vold.conf文件里的media_path路径可以设成
/devices/platform/pxa2xx-mci.0/mmc_host/mmc0/mmc0:e624
/devices/platform/pxa2xx-mci.0/mmc_host/mmc0/
/devices/platform/pxa2xx-mci.0/mmc_host/
/devices/platform/pxa2xx-mci.0/
/devices/platform/
/devices/
都行
9.5.2 命令

9.5.2.1 发送命令

文件:system/core/vold/Vold.c
int send_msg(char* message)
{
    int result = -1;
 
    pthread_mutex_lock(&write_mutex);
 
//    LOG_VOL("send_msg(%s):", message);
 
    if (fw_sock >= 0)
        result = write(fw_sock, message, strlen(message) + 1);
 
    pthread_mutex_unlock(&write_mutex);
 
    return result;
}
 
int send_msg_with_data(char *message, char *data)
{
    int result = -1;
 
    char* buffer = (char *)alloca(strlen(message) + strlen(data) + 1);
    if (!buffer) {
        LOGE("alloca failed in send_msg_with_data");
        return -1;
    }
 
    strcpy(buffer, message);
    strcat(buffer, data);
    return send_msg(buffer);
}
将消息写到sock文件
9.5.2.2 接收命令

文件:system/core/vold/Cmd_dispatch.c
int process_framework_command(int socket)
{
    int rc;
    char buffer[101];
 
    if ((rc = read(socket, buffer, sizeof(buffer) -1)) < 0) {
        LOGE("Unable to read framework command (%s)", strerror(errno));
        return -errno;
    } else if (!rc)
        return -ECONNRESET;
 
    int start = 0;
    int i;
 
    buffer[rc] = 0;
 
    for (i = 0; i < rc; i++) {
        if (buffer[i] == 0) {
            dispatch_cmd(buffer + start);
            start = i + 1;
        }
    }
    return 0;
}
读出socket文件的数据,然后对数据所代表的命令进行分配。
9.5.2.3 分配命令

文件:system/core/vold/Cmd_dispatch.c
// These must match the strings in java/android/android/os/UsbListener.java
#define VOLD_CMD_ENABLE_UMS         "enable_ums"
#define VOLD_CMD_DISABLE_UMS        "disable_ums"
#define VOLD_CMD_SEND_UMS_STATUS    "send_ums_status"
 
// these commands should contain a volume mount point after the colon
#define VOLD_CMD_MOUNT_VOLUME       "mount_volume:"
#define VOLD_CMD_EJECT_MEDIA        "eject_media:"
#define VOLD_CMD_FORMAT_MEDIA       "format_media:"
 
static struct cmd_dispatch dispatch_table[] = {
    { VOLD_CMD_ENABLE_UMS,      do_set_ums_enable },
    { VOLD_CMD_DISABLE_UMS,     do_set_ums_enable },
    { VOLD_CMD_SEND_UMS_STATUS, do_send_ums_status },
    { VOLD_CMD_MOUNT_VOLUME,    do_mount_volume },
    { VOLD_CMD_EJECT_MEDIA,     do_eject_media },
    { VOLD_CMD_FORMAT_MEDIA,    do_format_media },
    { NULL, NULL }
};
static void dispatch_cmd(char *cmd)
{
    struct cmd_dispatch *c;
 
    LOG_VOL("dispatch_cmd(%s):", cmd);
 
    for (c = dispatch_table; c->cmd != NULL; c++) {
        if (!strncmp(c->cmd, cmd, strlen(c->cmd))) {
            c->dispatch(cmd);
            return;
        }
    }
 
    LOGE("No cmd handlers defined for '%s'", cmd);
}
命令格式:mount_volume:/sdcard ,将mount_volume分配表dispatch_table中的命令字进行匹配,合适的进行处理。
9.5.2.4 处理命令

文件:system/core/vold/Cmd_dispatch.c
static int do_mount_volume(char *cmd)
{
    return volmgr_start_volume_by_mountpoint(&cmd[strlen("mount_volume:")]);
}
过滤掉命令字,将其后的参数传递给相应的处理函数。
9.5.3 socket

9.5.3.1 socket的服务程序

文件:system/core/vold/Vold.c
#define VOLD_SOCKET "vold"
nt main(int argc, char **argv)
{
    int door_sock = -1;
    int uevent_sock = -1;
    struct sockaddr_nl nladdr;
    int uevent_sz = 64 * 1024;
 
    LOGI("Android Volume Daemon version %d.%d", ver_major, ver_minor);
 
    /*
     * Create all the various sockets we'll need
     */
 
    // Socket to listen on for incomming framework connections
    if ((door_sock = android_get_control_socket(VOLD_SOCKET)) < 0) {
        LOGE("Obtaining file descriptor socket '%s' failed: %s",
             VOLD_SOCKET, strerror(errno));
        exit(1);
    }
 
    if (listen(door_sock, 4) < 0) {
        LOGE("Unable to listen on fd '%d' for socket '%s': %s",
             door_sock, VOLD_SOCKET, strerror(errno));
        exit(1);
    }
    ...
    // Socket to listen on for uevent changes
    memset(&nladdr, 0, sizeof(nladdr));
    nladdr.nl_family = AF_NETLINK;
    nladdr.nl_pid = getpid();
    nladdr.nl_groups = 0xffffffff;
 
    if ((uevent_sock = socket(PF_NETLINK,
                             SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0) {
        LOGE("Unable to create uevent socket: %s", strerror(errno));
        exit(1);
    }
 
    if (setsockopt(uevent_sock, SOL_SOCKET, SO_RCVBUFFORCE, &uevent_sz,
                   sizeof(uevent_sz)) < 0) {
        LOGE("Unable to set uevent socket options: %s", strerror(errno));
        exit(1);
    }
 
    if (bind(uevent_sock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {
        LOGE("Unable to bind uevent socket: %s", strerror(errno));
        exit(1);
    }
...
    while(1) {
        fd_set read_fds;
        struct timeval to;
        int max = 0;
        int rc = 0;
 
        to.tv_sec = (60 * 60);
        to.tv_usec = 0;
 
        FD_ZERO(&read_fds);
        FD_SET(door_sock, &read_fds);
        if (door_sock > max)
            max = door_sock;
        FD_SET(uevent_sock, &read_fds);
        if (uevent_sock > max)
            max = uevent_sock;
 
        if (fw_sock != -1) {
            FD_SET(fw_sock, &read_fds);
            if (fw_sock > max)
                max = fw_sock;
        }
 
        if ((rc = select(max + 1, &read_fds, NULL, NULL, &to)) < 0) {
            LOGE("select() failed (%s)", strerror(errno));
            sleep(1);
            continue;
        }
 
        if (!rc) {
            continue;
        }
 
        if (FD_ISSET(door_sock, &read_fds)) {
            struct sockaddr addr;
            socklen_t alen;
 
            alen = sizeof(addr);
 
            if (fw_sock != -1) {
                LOGE("Dropping duplicate framework connection");
                int tmp = accept(door_sock, &addr, &alen);
                close(tmp);
                continue;
            }
 
            if ((fw_sock = accept(door_sock, &addr, &alen)) < 0) {
                LOGE("Unable to accept framework connection (%s)",
                     strerror(errno));
            }
            LOG_VOL("Accepted connection from framework");
            if ((rc = volmgr_send_states()) < 0) {
                LOGE("Unable to send volmgr status to framework (%d)", rc);
            }
        }
 
        if (FD_ISSET(fw_sock, &read_fds)) {
            if ((rc = process_framework_command(fw_sock)) < 0) {
                if (rc == -ECONNRESET) {
                    LOGE("Framework disconnected");
                    close(fw_sock);
                    fw_sock = -1;
                } else {
                    LOGE("Error processing framework command (%s)",
                         strerror(errno));
                }
            }
        }
 
        if (FD_ISSET(uevent_sock, &read_fds)) {
            if ((rc = process_uevent_message(uevent_sock)) < 0) {
                LOGE("Error processing uevent msg (%s)", strerror(errno));
            }
        }
    } // while
 
}
以上代码有两个socket,一个是”vold” socket,负责接收和处理vold命令;一个是uevent socket 负责接收和处理uevent事件。
9.5.3.2 “vold” socket 客户程序

文件:frameworks/base/services/java/com/android/server/MountListener.java
private void listenToSocket() {
       LocalSocket socket = null;
 
        try {
            socket = new LocalSocket();
            LocalSocketAddress address = new LocalSocketAddress(VOLD_SOCKET,
                    LocalSocketAddress.Namespace.RESERVED);
 
            socket.connect(address);
       ...
 
9.6 mount SDCARD

MountListener.java 启动后会去挂载SDCARD.
文件:frameworks/base/services/java/com/android/server/MountListener.java
private static final String VOLD_CMD_SEND_UMS_STATUS = "send_ums_status";
private static final String VOLD_CMD_MOUNT_VOLUME = "mount_volume:";
private void listenToSocket() {
       LocalSocket socket = null;
 
        try {
            socket = new LocalSocket();
            LocalSocketAddress address = new LocalSocketAddress(VOLD_SOCKET,
                    LocalSocketAddress.Namespace.RESERVED);
 
            socket.connect(address);
                     writeCommand(VOLD_CMD_SEND_UMS_STATUS);
            mountMedia(Environment.getExternalStorageDirectory().getAbsolutePath());
                     ...
}
 
注:ExternalStorageDirectory的定义在:
文件:frameworks/base/core/java/android/os/Environment.java
private static final File EXTERNAL_STORAGE_DIRECTORY
            = getDirectory("EXTERNAL_STORAGE", "/sdcard");
 
public void mountMedia(String mountPoint) {
        writeCommand2(VOLD_CMD_MOUNT_VOLUME, mountPoint);
}
 
mountPoint = /sdcard
由此向”vold” socket发送一个VOLD_CMD_MOUNT_VOLUME命令,该命令处理如下:
文件:system/core/vold/Cmd_dispatch.c
static struct cmd_dispatch dispatch_table[] = {
    ...
    { VOLD_CMD_SEND_UMS_STATUS, do_send_ums_status },
{ VOLD_CMD_MOUNT_VOLUME,    do_mount_volume },
...
};
static int do_mount_volume(char *cmd)
{
    return volmgr_start_volume_by_mountpoint(&cmd[strlen("mount_volume:")]);
}
文件:system/core/vold/Volmgr.c
int volmgr_start_volume_by_mountpoint(char *mount_point)
{
       volume_t *v;
 
    v = volmgr_lookup_volume_by_mountpoint(mount_point, true);
    ...
 
        if (_volmgr_consider_disk_and_vol(v, v->dev->disk) < 0) {
            LOGE("volmgr failed to start volume '%s'", v->mount_point);
        }
    ...
    return 0;
}
static volume_t *volmgr_lookup_volume_by_mountpoint(char *mount_point, boolean leave_locked)
{
    volume_t *v = vol_root;
 
    while(v) {
        pthread_mutex_lock(&v->lock);
        if (!strcmp(v->mount_point, mount_point)) {
            if (!leave_locked)
                pthread_mutex_unlock(&v->lock);
            return v;
        }
        pthread_mutex_unlock(&v->lock);
        v = v->next;
    }
    return NULL;
}
在vol_root链表里找到对应的结点。
static int _volmgr_consider_disk_and_vol(volume_t *vol, blkdev_t *dev)
{
    ...
            part = blkdev_lookup_by_devno(dev->major, ALIGN_MMC_MINOR(dev->minor) + 1);
        ...
            rc = _volmgr_start(vol, part);
    return rc;
}
 
struct volmgr_fstable_entry {
    char *name;
    int     (*identify_fn) (blkdev_t *dev);
    int     (*check_fn) (blkdev_t *dev);
    int     (*mount_fn) (blkdev_t *dev, struct volume *vol, boolean safe_mode);
    boolean case_sensitive_paths;
};
static struct volmgr_fstable_entry fs_table[] = {
//    { "ext3", ext_identify, ext_check, ext_mount , true },
    { "vfat", vfat_identify, vfat_check, vfat_mount , false },
    { NULL, NULL, NULL, NULL , false}
};
 
static int _volmgr_start(volume_t *vol, blkdev_t *dev)
{
...
for (fs = fs_table; fs->name; fs++) {
        if (!fs->identify_fn(dev))
            break;
    }
...
    return volmgr_start_fs(fs, vol, dev);
}
static int volmgr_start_fs(struct volmgr_fstable_entry *fs, volume_t *vol, blkdev_t *dev)
{
    ...
 
    pthread_create(&vol->worker_thread, &attr, volmgr_start_fs_thread, vol);
 
    return 0;
}
static void *volmgr_start_fs_thread(void *arg)
{
    ...
 
    rc = fs->mount_fn(dev, vol, safe_mode);
   ...
}
由此mount_fn转向vfat_mount。
文件:system/core/vold/Volmgr_vfat.c
int vfat_mount(blkdev_t *dev, volume_t *vol, boolean safe_mode)
{
    int flags, rc;
    char *devpath;
 
    devpath = blkdev_get_devpath(dev);
 
#if VFAT_DEBUG
    LOG_VOL("vfat_mount(%d:%d, %s, %d):", dev->major, dev->minor, vol->mount_point, safe_mode);
#endif
 
    flags = MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_DIRSYNC;
 
    if (vol->state == volstate_mounted) {
        LOG_VOL("Remounting %d:%d on %s, safe mode %d", dev->major,
                dev->minor, vol->mount_point, safe_mode);
        flags |= MS_REMOUNT;
    }
 
    /*
     * Note: This is a temporary hack. If the sampling profiler is enabled,
     * we make the SD card world-writable so any process can write snapshots.
     *
     * TODO: Remove this code once we have a drop box in system_server.
     */
    char value[PROPERTY_VALUE_MAX];
    property_get("persist.sampling_profiler", value, "");
    if (value[0] == '1') {
        LOGW("The SD card is world-writable because the"
            " 'persist.sampling_profiler' system property is set to '1'.");
        rc = mount(devpath, vol->mount_point, "vfat", flags,
                "utf8,uid=1000,gid=1015,fmask=000,dmask=000,shortname=mixed");
    } else {
        /*
         * The mount masks restrict access so that:
         * 1. The 'system' user cannot access the SD card at all -
         *    (protects system_server from grabbing file references)
         * 2. Group users can RWX
         * 3. Others can only RX
         */
        rc = mount(devpath, vol->mount_point, "vfat", flags,
                "utf8,uid=1000,gid=1015,fmask=702,dmask=702,shortname=mixed");
    }
 
    if (rc && errno == EROFS) {
        LOGE("vfat_mount(%d:%d, %s): Read only filesystem - retrying mount RO",
             dev->major, dev->minor, vol->mount_point);
        flags |= MS_RDONLY;
        rc = mount(devpath, vol->mount_point, "vfat", flags,
                   "utf8,uid=1000,gid=1015,fmask=702,dmask=702,shortname=mixed");
    }
 
    if (rc == 0) {
        char *lost_path;
        asprintf(&lost_path, "%s/LOST.DIR", vol->mount_point);
        if (access(lost_path, F_OK)) {
            /*
             * Create a LOST.DIR in the root so we have somewhere to put
             * lost cluster chains (fsck_msdos doesn't currently do this)
             */
            if (mkdir(lost_path, 0755)) {
                LOGE("Unable to create LOST.DIR (%s)", strerror(errno));
            }
        }
        free(lost_path);
    }
 
#if VFAT_DEBUG
    LOG_VOL("vfat_mount(%s, %d:%d): mount rc = %d", dev->major,k dev->minor,
            vol->mount_point, rc);
#endif
    free (devpath);
    return rc;
}
至此,SDCARD才真正被挂载。
9.7 switch USB

<<9.4挂载usb>>提到的simulate_uevent("switch", devpath, "add", uevent_params)产生add事件后,会在文件:system/core/vold/Cmd_dispatch.c里分配一个处理,如下:
static int handle_switch_event(struct uevent *event)
{
    char *name = get_uevent_param(event, "SWITCH_NAME");
    char *state = get_uevent_param(event, "SWITCH_STATE");
 
 
    if (!strcmp(name, "usb_mass_storage")) {
        if (!strcmp(state, "online")) {
            ums_hostconnected_set(true);
        } else {
            ums_hostconnected_set(false);
            volmgr_enable_ums(false);
        }
}
...
}
文件:system/core/vold/Ums.c
void ums_hostconnected_set(boolean connected)
{
    ...
    send_msg(connected ? VOLD_EVT_UMS_CONNECTED : VOLD_EVT_UMS_DISCONNECTED);
}
在这里向“vold”socket发送一个VOLD_EVT_UMS_CONNECTED消息。
该消息在如下文件得到处理:
文件:frameworks/base/services/java/com/android/server/MountListener.java
    // vold commands
    private static final String VOLD_CMD_ENABLE_UMS = "enable_ums";
    private static final String VOLD_CMD_DISABLE_UMS = "disable_ums";
    private static final String VOLD_CMD_SEND_UMS_STATUS = "send_ums_status";
    ...
    // vold events
    private static final String VOLD_EVT_UMS_ENABLED = "ums_enabled";
    private static final String VOLD_EVT_UMS_DISABLED = "ums_disabled";
    private static final String VOLD_EVT_UMS_CONNECTED = "ums_connected";
private static final String VOLD_EVT_UMS_DISCONNECTED = "ums_disconnected";
private void listenToSocket() {
       LocalSocket socket = null;
 
        try {
            socket = new LocalSocket();
            LocalSocketAddress address = new LocalSocketAddress(VOLD_SOCKET,
                    LocalSocketAddress.Namespace.RESERVED);
 
            socket.connect(address);
 
            InputStream inputStream = socket.getInputStream();
            mOutputStream = socket.getOutputStream();
...
          
            while (true) {
                int count = inputStream.read(buffer);
                if (count < 0) break;
 
                int start = 0;
                for (int i = 0; i < count; i++) {
                    if (buffer[i] == 0) {
                        String event = new String(buffer, start, i - start);
                        handleEvent(event);
                        start = i + 1;
                    }                 
                }
            }              
        }
...
}
private void handleEvent(String event) {
        if (Config.LOGD) Log.d(TAG, "handleEvent " + event);
  
        int colonIndex = event.indexOf(':');
        String path = (colonIndex > 0 ? event.substring(colonIndex + 1) : null);
      
        ...
}else if (event.equals(VOLD_EVT_UMS_CONNECTED)) {
            mUmsConnected = true;
            mService.notifyUmsConnected();
        } ...
}
经mService(mount service) 的notifyUmsConnected() 广播一下状态,再绕回MountListenser调用如下函数发送一个连接USB的命令:
void setMassStorageEnabled(boolean enable) {
        writeCommand(enable ? VOLD_CMD_ENABLE_UMS : VOLD_CMD_DISABLE_UMS);
    }
文件:frameworks/base/services/java/com/android/server/MountService.java
void notifyUmsConnected() {
       ...
setMassStorageEnabled(true);
       ...
    }
public void setMassStorageEnabled(boolean enable) throws RemoteException {
        mListener.setMassStorageEnabled(enable);
    }
由此向”vold” socket 发送一个VOLD_CMD_ENABLE_UMS 的命令,该命令处理如下:
{ VOLD_CMD_ENABLE_UMS,      do_set_ums_enable },
文件:system/core/vold/Cmd_dispatch.c
static struct cmd_dispatch dispatch_table[] = {
    { VOLD_CMD_ENABLE_UMS,      do_set_ums_enable },
{ VOLD_CMD_DISABLE_UMS,     do_set_ums_enable },
...
static int do_set_ums_enable(char *cmd)
{
    if (!strcmp(cmd, VOLD_CMD_ENABLE_UMS))
        return volmgr_enable_ums(true);
 
    return volmgr_enable_ums(false);
}
转向文件:system/core/vold/Volmgr.c
int volmgr_enable_ums(boolean enable)
{
    volume_t *v = vol_root;
 
    while(v) {
        if (v->ums_path) {
            int rc;
 
            if (enable) {
                pthread_mutex_lock(&v->lock);
                            if (v->state == volstate_mounted)
                    volmgr_send_eject_request(v);
                ...
 
                // Stop the volume, and enable UMS in the callback
                rc = volmgr_shutdown_volume(v, _cb_volstopped_for_ums_enable, false);
                ...
                     }
        }
 next_vol:
        v = v->next;
    }
    return 0;
}
static void _cb_volstopped_for_ums_enable(volume_t *v, void *arg)
{
    ...
 
if ((rc = ums_enable(devdir_path, v->ums_path)) < 0)
...
}
再转向文件:system/core/vold/Ums.c
int ums_enable(char *dev_fspath, char *lun_syspath)
{
    LOG_VOL("ums_enable(%s, %s):", dev_fspath, lun_syspath);
 
    int fd;
    char filename[255];
 
    sprintf(filename, "/sys/%s/file", lun_syspath);
    if ((fd = open(filename, O_WRONLY)) < 0) {
        LOGE("Unable to open '%s' (%s)", filename, strerror(errno));
        return -errno;
    }
 
    if (write(fd, dev_fspath, strlen(dev_fspath)) < 0) {
        LOGE("Unable to write to ums lunfile (%s)", strerror(errno));
        close(fd);
        return -errno;
    }
  
    close(fd);
    return 0;
}
9.8 总结

vold.conf 配置了SDCARD和USB的系统设备路径。
volume_sdcard {
    ## This is the direct uevent device path to the SD slot on the device
    media_path     /devices/platform/msm_sdcc.2/mmc_host/mmc1
    emu_media_path /devices/platform/goldfish_mmc.0/mmc_host/mmc0
 
    media_type     mmc
    mount_point    /sdcard
    ums_path       /devices/platform/usb_mass_storage/lun0
}
vold程序启动后会加载SDCARD卡到 /sdcard 目录下;media_path的路径是cd /sys/class/mmc_host/mmc?/ 后使用pwd所获得的真实路径。
当有USB连接时,被要求使用MASS STORAGE模式时,则会停止SDCARD作为内部,而作为USB存储器。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值