Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 487be61f authored by Jerry Zhang's avatar Jerry Zhang
Browse files

Refactored Mtp driver interface into multiple classes.

Added new interface for FunctionFS. This allows most of
the driver code to exist in userspace. The driver will
automatically use FunctionFS if it is enabled for that
device, otherwise it will default to the kernel driver.

The intention is to eventually deprecate the kernel driver.

Bug: 30976142
Change-Id: I36b8d16ca254fddd995b3ea1bd3d37b0ff4a28f7
Test: New automated tests for MtpFfsHandle, AsyncIO.
Manual testing on each device.
parent b985e3c6
Loading
Loading
Loading
Loading
+8 −3
Original line number Diff line number Diff line
@@ -19,26 +19,31 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_SRC_FILES:=                                       \
                  AsyncIO.cpp                           \
                  MtpDataPacket.cpp                     \
                  MtpDebug.cpp                          \
                  MtpDevHandle.cpp                      \
                  MtpDevice.cpp                         \
                  MtpEventPacket.cpp                    \
                  MtpDeviceInfo.cpp                     \
                  MtpEventPacket.cpp                    \
                  MtpFfsHandle.cpp                      \
                  MtpObjectInfo.cpp                     \
                  MtpPacket.cpp                         \
                  MtpProperty.cpp                       \
                  MtpRequestPacket.cpp                  \
                  MtpResponsePacket.cpp                 \
                  MtpServer.cpp                         \
                  MtpStorage.cpp                        \
                  MtpStorageInfo.cpp                    \
                  MtpStringBuffer.cpp                   \
                  MtpStorage.cpp                        \
                  MtpUtils.cpp                          \

LOCAL_MODULE:= libmtp

LOCAL_CFLAGS := -DMTP_DEVICE -DMTP_HOST -Wall -Wextra -Werror

LOCAL_SHARED_LIBRARIES := libutils libcutils liblog libusbhost libbinder
LOCAL_SHARED_LIBRARIES := libbase libutils libcutils liblog libusbhost libbinder

include $(BUILD_SHARED_LIBRARY)

include $(call all-makefiles-under,$(LOCAL_PATH))

media/mtp/AsyncIO.cpp

0 → 100644
+176 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <android-base/logging.h>
#include <condition_variable>
#include <memory>
#include <mutex>
#include <queue>

#include "AsyncIO.h"

namespace {

void read_func(struct aiocb *aiocbp) {
    aiocbp->ret = TEMP_FAILURE_RETRY(pread(aiocbp->aio_fildes,
                aiocbp->aio_buf, aiocbp->aio_nbytes, aiocbp->aio_offset));
    if (aiocbp->ret == -1) aiocbp->error = errno;
}

void write_func(struct aiocb *aiocbp) {
    aiocbp->ret = TEMP_FAILURE_RETRY(pwrite(aiocbp->aio_fildes,
                aiocbp->aio_buf, aiocbp->aio_nbytes, aiocbp->aio_offset));
    if (aiocbp->ret == -1) aiocbp->error = errno;
}

void splice_read_func(struct aiocb *aiocbp) {
    aiocbp->ret = TEMP_FAILURE_RETRY(splice(aiocbp->aio_fildes,
                (off64_t*) &aiocbp->aio_offset, aiocbp->aio_sink,
                NULL, aiocbp->aio_nbytes, 0));
    if (aiocbp->ret == -1) aiocbp->error = errno;
}

void splice_write_func(struct aiocb *aiocbp) {
    aiocbp->ret = TEMP_FAILURE_RETRY(splice(aiocbp->aio_fildes, NULL,
                aiocbp->aio_sink, (off64_t*) &aiocbp->aio_offset,
                aiocbp->aio_nbytes, 0));
    if (aiocbp->ret == -1) aiocbp->error = errno;
}

std::queue<std::unique_ptr<struct aiocb>> queue;
std::mutex queue_lock;
std::condition_variable queue_cond;
std::condition_variable write_cond;
int done = 1;
void splice_write_pool_func(int) {
    while(1) {
        std::unique_lock<std::mutex> lk(queue_lock);
        queue_cond.wait(lk, []{return !queue.empty() || done;});
        if (queue.empty() && done) {
            return;
        }
        std::unique_ptr<struct aiocb> aiocbp = std::move(queue.front());
        queue.pop();
        lk.unlock();
        write_cond.notify_one();
        splice_write_func(aiocbp.get());
        close(aiocbp->aio_fildes);
    }
}

void write_pool_func(int) {
    while(1) {
        std::unique_lock<std::mutex> lk(queue_lock);
        queue_cond.wait(lk, []{return !queue.empty() || done;});
        if (queue.empty() && done) {
            return;
        }
        std::unique_ptr<struct aiocb> aiocbp = std::move(queue.front());
        queue.pop();
        lk.unlock();
        write_cond.notify_one();
        aiocbp->ret = TEMP_FAILURE_RETRY(pwrite(aiocbp->aio_fildes,
                    aiocbp->aio_pool_buf.get(), aiocbp->aio_nbytes, aiocbp->aio_offset));
        if (aiocbp->ret == -1) aiocbp->error = errno;
    }
}

constexpr int NUM_THREADS = 1;
constexpr int MAX_QUEUE_SIZE = 10;
std::thread pool[NUM_THREADS];

} // end anonymous namespace

void aio_pool_init(void(f)(int)) {
    CHECK(done == 1);
    done = 0;
    for (int i = 0; i < NUM_THREADS; i++) {
        pool[i] = std::thread(f, i);
    }
}

void aio_pool_splice_init() {
    aio_pool_init(splice_write_pool_func);
}

void aio_pool_write_init() {
    aio_pool_init(write_pool_func);
}

void aio_pool_end() {
    done = 1;
    for (int i = 0; i < NUM_THREADS; i++) {
        std::unique_lock<std::mutex> lk(queue_lock);
        lk.unlock();
        queue_cond.notify_one();
    }

    for (int i = 0; i < NUM_THREADS; i++) {
        pool[i].join();
    }
}

// used for both writes and splices depending on which init was used before.
int aio_pool_write(struct aiocb *aiocbp) {
    std::unique_lock<std::mutex> lk(queue_lock);
    write_cond.wait(lk, []{return queue.size() < MAX_QUEUE_SIZE;});
    queue.push(std::unique_ptr<struct aiocb>(aiocbp));
    lk.unlock();
    queue_cond.notify_one();
    return 0;
}

int aio_read(struct aiocb *aiocbp) {
    aiocbp->thread = std::thread(read_func, aiocbp);
    return 0;
}

int aio_write(struct aiocb *aiocbp) {
    aiocbp->thread = std::thread(write_func, aiocbp);
    return 0;
}

int aio_splice_read(struct aiocb *aiocbp) {
    aiocbp->thread = std::thread(splice_read_func, aiocbp);
    return 0;
}

int aio_splice_write(struct aiocb *aiocbp) {
    aiocbp->thread = std::thread(splice_write_func, aiocbp);
    return 0;
}

int aio_error(const struct aiocb *aiocbp) {
    return aiocbp->error;
}

ssize_t aio_return(struct aiocb *aiocbp) {
    return aiocbp->ret;
}

int aio_suspend(struct aiocb *aiocbp[], int n,
        const struct timespec *) {
    for (int i = 0; i < n; i++) {
        aiocbp[i]->thread.join();
    }
    return 0;
}

int aio_cancel(int, struct aiocb *) {
    // Not implemented
    return -1;
}

media/mtp/AsyncIO.h

0 → 100644
+77 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef _ASYNCIO_H
#define _ASYNCIO_H

#include <fcntl.h>
#include <linux/aio_abi.h>
#include <memory>
#include <signal.h>
#include <sys/cdefs.h>
#include <sys/types.h>
#include <time.h>
#include <thread>
#include <unistd.h>

/**
 * Provides a subset of POSIX aio operations, as well
 * as similar operations with splice and threadpools.
 */

struct aiocb {
    int aio_fildes;     // Assumed to be the source for splices
    void *aio_buf;      // Unused for splices

    // Used for threadpool operations only, freed automatically
    std::unique_ptr<char[]> aio_pool_buf;

    off_t aio_offset;
    size_t aio_nbytes;

    int aio_sink;       // Unused for non splice r/w

    // Used internally
    std::thread thread;
    ssize_t ret;
    int error;
};

// Submit a request for IO to be completed
int aio_read(struct aiocb *);
int aio_write(struct aiocb *);
int aio_splice_read(struct aiocb *);
int aio_splice_write(struct aiocb *);

// Suspend current thread until given IO is complete, at which point
// its return value and any errors can be accessed
int aio_suspend(struct aiocb *[], int, const struct timespec *);
int aio_error(const struct aiocb *);
ssize_t aio_return(struct aiocb *);
int aio_cancel(int, struct aiocb *);

// Initialize a threadpool to perform IO. Only one pool can be
// running at a time.
void aio_pool_write_init();
void aio_pool_splice_init();
// Suspend current thread until all queued work is complete, then ends the threadpool
void aio_pool_end();
// Submit IO work for the threadpool to complete. Memory associated with the work is
// freed automatically when the work is complete.
int aio_pool_write(struct aiocb *);

#endif // ASYNCIO_H

media/mtp/IMtpHandle.h

0 → 100644
+47 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifndef _IMTP_HANDLE_H
#define _IMTP_HANDLE_H

#include <linux/usb/f_mtp.h>

constexpr char FFS_MTP_EP0[] = "/dev/usb-ffs/mtp/ep0";

class IMtpHandle {
public:
    // Return number of bytes read/written, or -1 and errno is set
    virtual int read(void *data, int len) = 0;
    virtual int write(const void *data, int len) = 0;

    // Return 0 if send/receive is successful, or -1 and errno is set
    virtual int receiveFile(mtp_file_range mfr) = 0;
    virtual int sendFile(mtp_file_range mfr) = 0;
    virtual int sendEvent(mtp_event me) = 0;

    // Return 0 if operation is successful, or -1 else
    virtual int start() = 0;
    virtual int configure(bool ptp) = 0;

    virtual void close() = 0;

    virtual ~IMtpHandle() {}
};

IMtpHandle *get_ffs_handle();
IMtpHandle *get_mtp_handle();

#endif // _IMTP_HANDLE_H
+8 −7
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@

#include "MtpDataPacket.h"
#include "MtpStringBuffer.h"
#include "IMtpHandle.h"

namespace android {

@@ -418,8 +419,8 @@ void MtpDataPacket::putString(const uint16_t* string) {
}

#ifdef MTP_DEVICE
int MtpDataPacket::read(int fd) {
    int ret = ::read(fd, mBuffer, MTP_BUFFER_SIZE);
int MtpDataPacket::read(IMtpHandle *h) {
    int ret = h->read(mBuffer, MTP_BUFFER_SIZE);
    if (ret < MTP_CONTAINER_HEADER_SIZE)
        return -1;
    mPacketSize = ret;
@@ -427,20 +428,20 @@ int MtpDataPacket::read(int fd) {
    return ret;
}

int MtpDataPacket::write(int fd) {
int MtpDataPacket::write(IMtpHandle *h) {
    MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize);
    MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA);
    int ret = ::write(fd, mBuffer, mPacketSize);
    int ret = h->write(mBuffer, mPacketSize);
    return (ret < 0 ? ret : 0);
}

int MtpDataPacket::writeData(int fd, void* data, uint32_t length) {
int MtpDataPacket::writeData(IMtpHandle *h, void* data, uint32_t length) {
    allocate(length + MTP_CONTAINER_HEADER_SIZE);
    memcpy(mBuffer + MTP_CONTAINER_HEADER_SIZE, data, length);
    length += MTP_CONTAINER_HEADER_SIZE;
    MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, length);
    MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA);
    int ret = ::write(fd, mBuffer, length);
    int ret = h->write(mBuffer, length);
    return (ret < 0 ? ret : 0);
}

Loading