2011년 11월 24일 목요일

android에서 USB Mass Storage 사용하기

ODROID-7/A에 USB Host가 있습니다.

거기에 USB 저장장치를 연결하여 자동으로 mnt/sdcard/external_storage란 폴더에 자동으로 마운트가 되도록 수정해 보려고 합니다.

ODROID-A에는 두개의 mmc가 있어서 galaxy s처럼 추가 외부 저장장치를 지원합니다.

vold와 MountService.java를 수정하였습니다.

vold.fstab에 bus node를 등록하면 UMS까지 지원하려고 했지만 usb mass 장치를 붙일때 마다 bus node의 인덱싱이 host1, host2 처럼 증가합니다.

vold의 구조가 동적 증가하는 장치를 지원하지 않는 구조 입니다.

fstab에 등록되어 있는 노드 객체를 만들고 등록된 Volume이 NetlinkEvent의 DEVPATH와 동일하면 처리합니다. 따라서 USB Host에 연결하는 장치와 같이 bus 이름이 증가하는 구조에는 적용이 안됩니다.

그리서 vold에서 강제로 mount 하도록 해 봤습니다.

kernel에 USB Mass Storage support를 추가 한다.

     <*>   USB Mass Storage support   



그리고 system/vold/VolumeManager.cpp에 handleBlockEvent() 함수를 다음과 같이 수정합니다.


bool usb_storage_mounted = false;
bool second_netlinkevent = false;


void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {
    const char *devpath = evt->findParam("DEVPATH");

    /* Lookup a volume to handle this device */
    VolumeCollection::iterator it;
    bool hit = false;
    for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {
        if (!(*it)->handleBlockEvent(evt)) {
#ifdef NETLINK_DEBUG
            SLOGD("Device '%s' event handled by volume %s\n", devpath, (*it)->getLabel());
#endif
            hit = true;
            break;
        }
    }

    if (!hit) {
#ifdef NETLINK_DEBUG
        SLOGW("No volumes handled block event for '%s'", devpath);
#endif


        if (second_netlinkevent) {
            second_netlinkevent = false;
            return;
        }

        char *usb_mass_node = "/devices/platform/s5p-ehci/usb";
        if (usb_storage_mounted) {
            umount("/mnt/sdcard/external_storage");
            usb_storage_mounted = false;
            SLOGE("unmounted USB Mass Storage");
        } else {
            int len = strlen(usb_mass_node);
            if (strncmp(devpath, usb_mass_node, len) == 0) {
                usleep(5000);
                if (Fat::doMount("/dev/block/sda1", "/mnt/sdcard/external_storage", false, false, false,
                 1000, 1015, 0702, true)) {
                    SLOGE("%s failed to mount via VFAT (%s)\n", devpath, strerror(errno));
                } else {
                    SLOGE("mounted USB Mass Storage");
                    usb_storage_mounted = true;
                }
            } else {
                SLOGE("not USB Mass Storage");
            }
        }

        second_netlinkevent = true;
    }

코드를 조금 설명 드리면 USB Mass는 event가 두번 발생하여 second_netlinkevent를 통해 한번만 처리되도록 하였습니다.

usleep은 event가 발생시점에는 node가 없을 수 있기 때문에 대기 하였다 mount 시킵니다.

devpath가 "/devices/platform/s5p-ehci/usb" 까지 동일하고 usb1,2,3... 이렇게 증가 하기 때문에 여기까지 같다면 usb mass storage 장치가 연결된 걸로 처리 합니다.

물론 init.rc에 external_storage 노드를 처리하여야 합니다.

 40 # create mountpoints
 41     mkdir /mnt 0775 root system
 42     mkdir /mnt/sdcard 0000 system system
 43     mkdir /mnt/sdcard/external_storage 0000 system system
 44 
 45 # Create cgroup mount point for cpu accounting
 46     mkdir /acct
 47     mount cgroup none /acct cpuacct
 48     mkdir /acct/uid
 49 
 50 # Backwards Compat - XXX: Going away in G*
 51     symlink /mnt/sdcard /sdcard
 52     symlink /mnt/sdcard/external_storage /sdcard1

그리고 vold에서 external_storage를 만드는 코드를 추가 합니다.

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 ", 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 ", false);
            return 0;
        }
        rc = vm->mountVolume(argv[2]);
        //codewalker
        mkdir("/mnt/sdcard/external_storage", 0777);
    } else if (!strcmp(argv[1], "unmount")) {
        if (argc < 3 || argc > 4 || (argc == 4 && strcmp(argv[3], "force"))) {
            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume unmount [force]", false);
            return 0;
        }

        bool force = false;
        if (argc >= 4 && !strcmp(argv[3], "force")) {



이렇게 하면 Media Scan이 안되는 문제와 UMS가 안되는 문제가 있습니다.
Astro FileManager 같은데서 직접 폴더를 열어서 사용하는 상황에 맞겠죠.

그리고 여럿 예외 처리가 필요할 걸로 판단됩니다.
댓글 쓰기