USB On-The-Go and Embedded Host
Virtually every portable device now uses USB for PC connectivity. As these products increase in popularity, there is a growing need for them to communicate both with USB peripherals and directly with each other when a PC is not available. There is also an increase in the number of other, non-PC hosts (Embedded Hosts) which support USB in order to connect to USB peripherals.
详细英文文档请查看:
http://www.usb.org/developers/onthego/。
下面具体看代码:
先看vold:android在vold里处理otg。即将设备写到一个标志文件,再进行广播大容量存储的状态。
vim /vold/CommandListener.cpp
int CommandListener::VolumeCmd::runCommand(SocketClient *cli, int argc, char **argv) { dumpArgs(argc, argv, -1); if (argc < 2) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false); return 0; } VolumeManager *vm = VolumeManager::Instance(); int rc = 0; if (!strcmp(argv[1], "list")) { return vm->listVolumes(cli); } else if (!strcmp(argv[1], "debug")) { if (argc != 3 || (argc == 3 && (strcmp(argv[2], "off") && strcmp(argv[2], "on")))) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume debug <off/on>", false); return 0; } vm->setDebug(!strcmp(argv[2], "on") ? true : false); } else if (!strcmp(argv[1], "mount")) { if (argc != 3) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume mount <path>", false); return 0; } rc = vm->mountVolume(argv[2]); } else if (!strcmp(argv[1], "unmount")) { if (argc < 3 || argc > 4 || ((argc == 4 && strcmp(argv[3], "force")) && (argc == 4 && strcmp(argv[3], "force_and_revert")))) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume unmount <path> [force|force_and_revert]", false); return 0; } bool force = false; bool revert = false; if (argc >= 4 && !strcmp(argv[3], "force")) { force = true; } else if (argc >= 4 && !strcmp(argv[3], "force_and_revert")) { force = true; revert = true; } rc = vm->unmountVolume(argv[2], force, revert); } else if (!strcmp(argv[1], "format")) { if (argc != 3) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume format <path>", false); return 0; } rc = vm->formatVolume(argv[2]); } else if (!strcmp(argv[1], "share")) { if (argc != 4) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume share <path> <method>", false); return 0; } rc = vm->shareVolume(argv[2], argv[3]); //该函数判断SD卡状态,若不存在或处于忙状态,怎返回失败值。 } else if (!strcmp(argv[1], "unshare")) { if (argc != 4) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume unshare <path> <method>", false); return 0; } rc = vm->unshareVolume(argv[2], argv[3]); } else if (!strcmp(argv[1], "shared")) { bool enabled = false; if (argc != 4) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume shared <path> <method>", false); return 0; } if (vm->shareEnabled(argv[2], argv[3], &enabled)) { cli->sendMsg( ResponseCode::OperationFailed, "Failed to determine share enable state", true); } else { cli->sendMsg(ResponseCode::ShareEnabledResult, (enabled ? "Share enabled" : "Share disabled"), false); } return 0; } else { cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume cmd", false); } if (!rc) { cli->sendMsg(ResponseCode::CommandOkay, "volume operation succeeded", false); } else { int erno = errno; rc = ResponseCode::convertFromErrno(); cli->sendMsg(rc, "volume operation failed", true); } return 0;}
往下继续:
vi ./VolumeManager.cpp
#define MASS_STORAGE_FILE_PATH "/sys/class/android_usb/android0/f_mass_storage/lun/file"
1、连接PC机
int VolumeManager::shareVolume(const char *label, const char *method) { Volume *v = lookupVolume(label); if (!v) { errno = ENOENT; return -1; } /* * Eventually, we'll want to support additional share back-ends, * some of which may work while the media is mounted. For now, * we just support UMS */ if (strcmp(method, "ums")) { errno = ENOSYS; return -1; } if (v->getState() == Volume::State_NoMedia) { errno = ENODEV; return -1; } if (v->getState() != Volume::State_Idle) { // You need to unmount manually befoe sharing errno = EBUSY; return -1; } if (mVolManagerDisabled) { errno = EBUSY; return -1; } /*getShareDevice该函数直接返回SD卡的设备号,部分版本使用函数getDiskDevice函数名*/ dev_t d = v->getShareDevice(); if ((MAJOR(d) == 0) && (MINOR(d) == 0)) { // This volume does not support raw disk access errno = EINVAL; return -1; } int fd; char nodepath[255]; snprintf(nodepath, sizeof(nodepath), "/dev/block/vold/%d:%d", MAJOR(d), MINOR(d)); /*这里是该文件关键之处,想要让PC机识别到设备上的SD卡或者Iland,那么就必须将SD卡的设备节点路径写到以下文件中去,这样才能打开大容量存储功能*/ if ((fd = open(MASS_STORAGE_FILE_PATH, O_WRONLY)) < 0) { SLOGE("Unable to open ums lunfile (%s)", strerror(errno)); return -1; } if (write(fd, nodepath, strlen(nodepath)) < 0) { SLOGE("Unable to write to ums lunfile (%s)", strerror(errno)); close(fd); return -1; } close(fd);
/**handleVolumeShared 函数在Volume中,是一个虚函数,在子类DirectVolume中实现,也就是广播ums状态/ v->handleVolumeShared();
/*注:adnroid4.0以上版本增加了以下判断*/ if (mUmsSharingCount++ == 0) { FILE* fp; mSavedDirtyRatio = -1; // in case we fail if ((fp = fopen("/proc/sys/vm/dirty_ratio", "r+"))) { char line[16]; if (fgets(line, sizeof(line), fp) && sscanf(line, "%d", &mSavedDirtyRatio)) { fprintf(fp, "%d\n", mUmsDirtyRatio); } else { SLOGE("Failed to read dirty_ratio (%s)", strerror(errno)); } fclose(fp); } else { SLOGE("Failed to open /proc/sys/vm/dirty_ratio (%s)", strerror(errno)); } } return 0;}
2、断开PC机
int VolumeManager::unshareVolume(const char *label, const char *method) { Volume *v = lookupVolume(label); if (!v) { errno = ENOENT; return -1; } if (strcmp(method, "ums")) { errno = ENOSYS; return -1; } if (v->getState() != Volume::State_Shared) { errno = EINVAL; return -1; } int fd; if ((fd = open(MASS_STORAGE_FILE_PATH, O_WRONLY)) < 0) { SLOGE("Unable to open ums lunfile (%s)", strerror(errno)); return -1; } /*2.3版本使用ums模式,断开连接时可以在PC端进行断开,android设备会自动挂载SD,这是使用ums的弊端,PC和androd设备不能同时对SD卡进行操作。andorid4.0以上版本升级为MTP模式,可以同时操作,后期再升级。
断开电脑,进行android设备端操作时,会将MASS_STORAGE_FILE_PATH写为空*/ char ch = 0; if (write(fd, &ch, 1) < 0) { SLOGE("Unable to write to ums lunfile (%s)", strerror(errno)); close(fd); return -1; } close(fd); /*同连接状态一样,断开U盘挂载模式*/
v->handleVolumeUnshared(); if (--mUmsSharingCount == 0 && mSavedDirtyRatio != -1) { FILE* fp; if ((fp = fopen("/proc/sys/vm/dirty_ratio", "r+"))) { fprintf(fp, "%d\n", mSavedDirtyRatio); fclose(fp); } else { SLOGE("Failed to open /proc/sys/vm/dirty_ratio (%s)", strerror(errno)); } mSavedDirtyRatio = -1; } return 0;}
3、在处理好后,vold将继续向上Framework汇报处理结果
for (i = mVolumes->begin(); i != mVolumes->end(); ++i) { char *buffer; asprintf(&buffer, "%s %s %d", (*i)->getLabel(), (*i)->getMountpoint(), (*i)->getState()); cli->sendMsg(ResponseCode::VolumeListResult, buffer, false); free(buffer); } cli->sendMsg(ResponseCode::CommandOkay, "Volumes listed.", false);
以vold为突破点,继续往下看,看看kernel如何处理。