ADROID 2.1 架构解析 9 SD/USB

来源:互联网 发布:手机淘宝直播入口 编辑:程序博客网 时间:2024/05/01 13:47

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_pathsmedia_typemedia_typemount_pointums_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是一个链接路径,所以需要用ChdirGetcwd来获取它的真实路径。以例子路径来说,获得的真实路径是:

/sys/devices/platform/pxa2xx-mci.0/mmc_host/mmc0/mmc0:e624

所以DEVPATH = /devices/platform/pxa2xx-mci.0/mmc_host/mmc0/mmc0:e624,而这个也就是在配置文件vold.confmedia_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,DEVTYPEMAJORMINOR,然后将这四个参数作为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_rootmedia_pathvol_rootmedia_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_rootmedia_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 配置了SDCARDUSB的系统设备路径。

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存储器。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 新生儿5天拉稀水怎么办 10个月孩子拉肚子怎么办 不满月的宝宝拉肚子怎么办 一周岁宝宝发烧腹泻呕吐怎么办 6个月宝宝37度怎么办 1岁宝宝发烧37.2怎么办 新生儿发烧37度3怎么办 两个月宝宝抵抗力差怎么办 6月宝宝着凉拉稀怎么办 六个月的宝宝拉肚子怎么办 衣服颜色太深了怎么办 一多半宝宝爱喝水不爱吃饭怎么办 十个月宝宝不爱吃饭怎么办 十个月宝宝突然不爱吃饭怎么办 二十个月宝宝不爱吃饭怎么办 十个月的宝宝不爱吃饭怎么办 6年级学生数学差怎么办 打印机打不出来就是一张白纸怎么办 wps表格下拉数字不递增怎么办 wps表格圈怎么打出来怎么办 手表固定圈掉了怎么办 起来觉得头晕头胀怎么办? 孩子不好好写作业怎么办 孩子考试考差了怎么办 孩子计算题马虎大意怎么办 二年级孩子不认字怎么办 发现计算上的错误怎么办 孩子不好好做作业怎么办 手破了红肿了怎么办呢 老师反应孩子在校粗心胆小怎么办 四年级的学生计算粗心怎么办 老打孩子骂孩子怎么办 站久了脚肿了怎么办 孩子初中了书写越来越潦草怎么办 给孩子自由孩子无法无天怎么办 孩子挑食幼儿园老师该怎么办 老师说孩子挑食家长怎么办 工作中老是粗心不细心怎么办 小孩数学总是特别粗心该怎么办 孩子起范疙瘩的怎么办 做题马虎不认真怎么办