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存储器。
ADROID 2.1 架构解析 9 SD/USBk
最新推荐文章于 2021-01-14 15:14:46 发布